/********************************************************************* * * Copyright (C) 2002, 2004-2005, 2007-2008, Karlsruhe University * * File path: api/v4/ipcx.cc * Description: Extended transfer of IPC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ipcx.cc,v 1.26 2006/10/19 22:57:38 ud3 Exp $ * ********************************************************************/ #include <debug.h> #include <kdb/tracepoints.h> #include INC_API(tcb.h) #include INC_API(ipc.h) #include INC_API(fpage.h) #include INC_API(schedule.h) #include INC_GLUE(map.h) #define CHECK_BR_IDX(idx) if (idx > IPC_NUM_BR) goto message_overflow #define CHECK_MR_IDX(idx, total) if (idx > total) goto message_overflow DECLARE_TRACEPOINT_DETAIL(IPC_STRING_COPY); DECLARE_TRACEPOINT_DETAIL(IPC_STRING_ITEM); DECLARE_TRACEPOINT_DETAIL(IPC_MAPGRANT_ITEM); DECLARE_TRACEPOINT_DETAIL(IPC_MESSAGE_OVERFLOW); DECLARE_TRACEPOINT_DETAIL(IPC_EXT_TRANSFER); #if defined(CONFIG_X_CTRLXFER_MSG) DECLARE_TRACEPOINT_DETAIL(IPC_CTRLXFER_ITEM); DECLARE_TRACEPOINT_DETAIL(IPC_CTRLXFER_ITEM_DETAILS); #endif #if !defined(IPC_STRING_COPY) extern "C" void * memcpy (void * dst, const void * src, word_t len); #define IPC_STRING_COPY memcpy #endif word_t ipc_copy (tcb_t * src, addr_t src_addr, tcb_t * dst, addr_t dst_addr, word_t len) { TRACEPOINT (IPC_STRING_COPY, "IPC string copy: %t @ %p -> %t @ %p, len=0x%x\n", src, src_addr, dst, dst_addr, len); space_t * src_space = src->get_space (); space_t * dst_space = dst->get_space (); // Check limits of source and destination string. word_t src_len = src_space->get_copy_limit (src_addr, len); word_t dst_len = dst_space->get_copy_limit (dst_addr, len); // String might need to be clipped. if (src_len < len) len = src_len; if (dst_len < len) len = dst_len; // For inter-space string copy we need to use a copy area. if (src_space != dst_space) src->adjust_for_copy_area (dst, &src_addr, &dst_addr); src->misc.ipc_copy.copy_start_src = src->misc.ipc_copy.copy_fault = src_addr; src->misc.ipc_copy.copy_start_dst = dst_addr; // we may cause a nested pagefault ipc, therefore unlock the receiver tcb dst->unlock(); IPC_STRING_COPY (dst_addr, src_addr, len); dst->lock(); src->misc.ipc_copy.copy_length += len; return len; } static void copy_mr(tcb_t * dst, tcb_t * src, int index) { ASSERT(index < IPC_NUM_MR); dst->set_mr(index, src->get_mr(index)); } msg_tag_t extended_transfer(tcb_t * src, tcb_t * dst, msg_tag_t msgtag) { msg_item_t src_item; acceptor_t acceptor; int br_idx = 1; word_t total_mrs = msgtag.get_untyped() + msgtag.get_typed() + 1; word_t total_len = 0; #if defined(CONFIG_X_CTRLXFER_MSG) word_t total_cxfer_mrs = 0; #endif bool accept_strings; // ENABLE_TRACEPOINT (IPC_STRING_COPY, ~0, 0); // ENABLE_TRACEPOINT (IPC_STRING_ITEM, ~0, 0); // ENABLE_TRACEPOINT (IPC_MESSAGE_OVERFLOW, ~0, 0); #undef TRACEF #define TRACEF(args...) if (total_mrs > IPC_NUM_MR) { printf("message exceeds MR's (untyped=%d, typed=%d)\n", msgtag.get_untyped(), msgtag.get_typed()); enter_kdebug("message exceeds MR's"); goto message_overflow; } src->set_state (thread_state_t::locked_running); dst->set_state (thread_state_t::locked_waiting); src->set_partner (dst->get_global_id ()); src->misc.ipc_copy.copy_length = 0; acceptor = dst->get_br(0); /* does the receiver (still) accepts strings? */ accept_strings = acceptor.accept_strings(); TRACEPOINT(IPC_EXT_TRANSFER, "tag=%p, untyped: %d, typed: %d, acceptor: %x\n", msgtag.raw, msgtag.get_untyped(), msgtag.get_typed(), acceptor.raw); for (word_t src_idx = msgtag.get_untyped() + 1; src_idx < total_mrs; ) { src_item = src->get_mr(src_idx); #if defined(CONFIG_X_CTRLXFER_MSG) if (src_item.is_ctrlxfer_item()) { bool src_mr = !src->flags.is_set(tcb_t::kernel_ctrlxfer_msg); bool dst_mr = !acceptor.accept_ctrlxfer(); word_t cxfer_regs; TRACEPOINT(IPC_CTRLXFER_ITEM, "ctrlxfer item: ipc %t->%t id=%d, mask=%x %c->%c", src, dst, src_item.get_ctrlxfer_id(), src_item.get_ctrlxfer_mask(), src_mr ? 'm' : 'f', dst_mr ? 'm' : 'f'); cxfer_regs = src->ctrlxfer(dst, src_item, src_idx, msgtag.x.untyped+1+total_cxfer_mrs, src_mr, dst_mr); src_idx += src_mr ? cxfer_regs : 1; msgtag.x.typed += src_mr ? 0 : cxfer_regs - 1; total_cxfer_mrs += dst_mr ? 0 : cxfer_regs; } else #endif if (src_item.is_map_item() || src_item.is_grant_item()) { /* is the descriptor beyond the valid range? */ CHECK_MR_IDX(src_idx + 1, total_mrs); fpage_t snd_fpage, rcv_fpage; snd_fpage.raw = src->get_mr(src_idx + 1); if (snd_fpage.is_mempage ()) rcv_fpage.raw = acceptor.get_rcv_window(); else if (snd_fpage.is_archpage ()) rcv_fpage = acceptor.get_arch_specific_rcvwindow (dst); else // Unknown fpage type goto message_overflow; TRACEPOINT(IPC_MAPGRANT_ITEM, "%s item: snd_base=%p, fpage=%p\n", src_item.is_map_item() ? "map" : "grant", src_item.get_snd_base(), snd_fpage.raw); /* does the receiver accept mappings */ if (EXPECT_FALSE( rcv_fpage.is_nil_fpage() )) goto message_overflow; copy_mr(dst, src, src_idx++); copy_mr(dst, src, src_idx++); if (snd_fpage.is_mempage ()) { src->get_space()->map_fpage (snd_fpage, src_item.get_snd_base(), dst->get_space(), rcv_fpage, src_item.is_grant_item()); #if 0 && defined(CONFIG_X_X86_HVM) if (dst->get_space()->is_hvm_space()) src->get_space()->map_fpage (snd_fpage, src_item.get_snd_base(), dst->get_space()->get_hvm_space()->get_gpas(), rcv_fpage, src_item.is_grant_item()); #endif } else if (snd_fpage.is_archpage ()) arch_map_fpage(src, snd_fpage, src_item.get_snd_base (), dst, rcv_fpage, src_item.is_grant_item ()); if (EXPECT_FALSE (src_item.is_grant_item () && snd_fpage.get_size_log2 () > rcv_fpage.get_size_log2 ())) { /* * We are granting an fpage that is larger than * receive window. Make sure that the whole fpage of * the sender is flushed. We don't call unmap from * the map function itself to avoid deep function * recursion. */ if (snd_fpage.is_mempage ()) src->get_space ()->unmap_fpage (snd_fpage, true, false); else if (snd_fpage.is_archpage ()) arch_unmap_fpage (src, snd_fpage, true); } } else if (src_item.is_string_item()) { /* * Copy the MR at the very beginning to make sure the * receiver can deal with cut message situations. */ copy_mr (dst, src, src_idx); if (! accept_strings) goto message_overflow; // We might have overflow on MRs CHECK_MR_IDX (src_idx + src_item.get_string_ptr_count (), total_mrs); msg_item_t dst_item; word_t src_addr, src_len, src_ptridx; word_t dst_addr, dst_len, dst_ptridx; src_addr = src->get_mr (src_idx + 1); src_len = src_item.get_string_length (); src_ptridx = 1; // Check for overflow on BRs. br_idx is always guaranteed // to be in range. dst_item = dst->get_br (br_idx); CHECK_BR_IDX (br_idx + dst_item.get_string_ptr_count ()); dst_addr = dst->get_br (br_idx + 1); dst_len = dst_item.get_string_length (); dst_ptridx = 1; TRACEPOINT (IPC_STRING_ITEM, "IPC string item: src:%p (sub=%d idx=%d len=%p %s)" "dst:%p (sub=%d idx=%d len=%p %s)", src_item.raw, src_item.get_string_ptr_count (), src_ptridx, src_len, src_item.is_string_compound () ? "c" : "", dst_item.raw, dst_item.get_string_ptr_count (), dst_ptridx, dst_len, dst_item.is_string_compound () ? "c" : ""); // Sanity checking if (! dst_item.is_string_item ()) goto message_overflow; // Check if there are more receive buffers after this one accept_strings = dst_item.more_strings (); bool end_of_send_string = false; while (! end_of_send_string) { TRACEPOINT (IPC_STRING_ITEM, " src: addr=%p len=%p (idx=%d)\n" " dst: addr=%p len=%p (idx=%d)\n", src_addr, src_len, src_ptridx, dst_addr, dst_len, dst_ptridx); copy_mr (dst, src, src_idx + src_ptridx); word_t copy_length = dst_len < src_len ? dst_len : src_len; word_t cpy_len = ipc_copy (src, (addr_t) src_addr, dst, (addr_t) dst_addr, copy_length); total_len += cpy_len; // Copy operation might have been clipped. if (cpy_len < copy_length) goto message_overflow; src_len -= cpy_len; dst_len -= cpy_len; src_addr += cpy_len; dst_addr += cpy_len; if (src_len == 0) { // Current part of send string exhausted. Check // if there are more substrings or compund strings // in the current send string. if (src_ptridx < src_item.get_string_ptr_count ()) { // More substrings to send src_ptridx++; src_addr = src->get_mr (src_idx + src_ptridx); src_len = src_item.get_string_length (); } else if (src_item.is_string_compound ()) { // Send string is compund src_idx += src_ptridx + 1; src_ptridx = 1; CHECK_MR_IDX (src_idx + 1, total_mrs); src_item = src->get_mr (src_idx); src_addr = src->get_mr (src_idx + src_ptridx); src_len = src_item.get_string_length (); CHECK_MR_IDX (src_idx + src_item.get_string_ptr_count (), total_mrs); TRACEPOINT (IPC_STRING_ITEM, " src: substrings=%d (idx=%d) len=%p %s\n", src_item.get_string_ptr_count (), src_ptridx, src_len, src_item.is_string_compound () ? "compound" : ""); } else { // End of send string end_of_send_string = true; src_idx += src_ptridx + 1; } } if (end_of_send_string) { // No more data in the current send string. Skip // past the current receive buffer. TRACEF ("Skip receive buffer\n"); bool compound; do { compound = dst_item.is_string_compound (); TRACEF ("compund=%d more=%d\n", dst_item.is_string_compound (), dst_item.more_strings ()); // Calculate position of next string item br_idx += dst_item.get_string_ptr_count () + 1; if (br_idx >= IPC_NUM_BR) { // BR register overflow TRACEF ("Last receive buffer\n"); accept_strings = false; break; } dst_item.raw = dst->get_br (br_idx); } while (compound); } else { // More data in send string. Check if we have // room in the receive buffers. if (dst_len == 0) { if (dst_ptridx < dst_item.get_string_ptr_count ()) { // More substrings in receive buffer dst_ptridx++; dst_addr = dst->get_br (br_idx + dst_ptridx); dst_len = dst_item.get_string_length (); } else if (dst_item.is_string_compound ()) { // Receive buffer is compund br_idx += dst_ptridx + 1; dst_ptridx = 1; CHECK_BR_IDX (br_idx + 1); dst_item = dst->get_br (br_idx); dst_addr = dst->get_br (br_idx + dst_ptridx); dst_len = dst_item.get_string_length (); CHECK_BR_IDX (br_idx + dst_item.get_string_ptr_count ()); TRACEPOINT ( IPC_STRING_ITEM, " dst: substrings=%d (idx=%d)" " len=%p %s\n", dst_item.get_string_ptr_count (), dst_ptridx, src_len, dst_item.is_string_compound () ? "compound" : ""); } else { // No more receive buffers TRACEF ("No more receive buffers\n"); goto message_overflow; } } } } // while (! end_of_send_string) } else { TRACEF("unknown item type\n"); goto message_overflow; } } // Release copy area src->release_copy_area (); // Cancel any pending XFER timeouts. src->sched_state.cancel_timeout (); src->flags -= tcb_t::has_xfer_timeout; return msgtag; message_overflow: // Release copy area src->release_copy_area (); // Cancel any pending XFER timeouts. src->sched_state.cancel_timeout (); TRACEPOINT (IPC_MESSAGE_OVERFLOW, "IPC message overflow (%t->%t), len=0x%x\n", src, dst, total_len); // Report message overflow error dst->set_error_code (IPC_RCV_ERROR (ERR_IPC_MSG_OVERFLOW (total_len))); src->set_error_code (IPC_SND_ERROR (ERR_IPC_MSG_OVERFLOW (total_len))); msgtag.set_error(); return msgtag; }