github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/bcm2835/mailbox.go (about)

     1  // BCM2835 SoC Mailbox support
     2  // https://github.com/f-secure-foundry/tamago
     3  //
     4  // Use of this source code is governed by the license
     5  // that can be found in the LICENSE file.
     6  
     7  //
     8  // Mailboxes are used for inter-processor communication, in particular
     9  // the VideoCore processor.
    10  //
    11  
    12  package bcm2835
    13  
    14  import (
    15  	"encoding/binary"
    16  	"fmt"
    17  	"runtime"
    18  	"sync"
    19  
    20  	"github.com/f-secure-foundry/tamago/dma"
    21  	"github.com/f-secure-foundry/tamago/internal/reg"
    22  )
    23  
    24  // We reserve the 'gap' above excStack and below TEXT segment start for
    25  // mailbox usage.  There is nothing requiring use of this region, but it
    26  // is convenient since it is always available.  Other regions could be
    27  // used by adjusting ramSize to provide space at top of address range.
    28  const (
    29  	MAILBOX_REGION_BASE = 0xC000
    30  	MAILBOX_REGION_SIZE = 0x4000
    31  )
    32  
    33  // Registers for using mailbox
    34  const (
    35  	MAILBOX_BASE       = 0xB880
    36  	MAILBOX_READ_REG   = MAILBOX_BASE + 0x00
    37  	MAILBOX_STATUS_REG = MAILBOX_BASE + 0x18
    38  	MAILBOX_WRITE_REG  = MAILBOX_BASE + 0x20
    39  	MAILBOX_FULL       = 0x80000000
    40  	MAILBOX_EMPTY      = 0x40000000
    41  )
    42  
    43  type mailbox struct {
    44  	Region *dma.Region
    45  	Mutex  sync.Mutex
    46  }
    47  
    48  // Mailbox provides access to the BCM2835 mailbox used to communicate with
    49  // the VideoCore CPU
    50  var Mailbox = mailbox{}
    51  
    52  func init() {
    53  	// We don't use this region for DMA, but dma package provides a convenient
    54  	// block allocation system.
    55  	Mailbox.Region = &dma.Region{
    56  		Start: MAILBOX_REGION_BASE | DRAM_FLAG_NOCACHE,
    57  		Size:  MAILBOX_REGION_SIZE,
    58  	}
    59  
    60  	Mailbox.Region.Init()
    61  }
    62  
    63  type MailboxTag struct {
    64  	ID     uint32
    65  	Buffer []byte
    66  }
    67  
    68  type MailboxMessage struct {
    69  	MinSize int
    70  	Code    uint32
    71  	Tags    []MailboxTag
    72  }
    73  
    74  func (m *MailboxMessage) Error() bool {
    75  	return m.Code == 0x80000001
    76  }
    77  
    78  func (m *MailboxMessage) Tag(code uint32) *MailboxTag {
    79  	for i, tag := range m.Tags {
    80  		if tag.ID&0x7FFFFFFF == code&0x7FFFFFFF {
    81  			return &m.Tags[i]
    82  		}
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  // Call exchanges message via a mailbox channel
    89  //
    90  // The caller is responsible for ensuring the 'tags' in the
    91  // message have sufficient buffer allocated for the response
    92  // expected.  The response replaces the input message.
    93  func (mb *mailbox) Call(channel int, message *MailboxMessage) {
    94  	size := 8 // Message Header
    95  	for _, tag := range message.Tags {
    96  		// 3 word tag header + tag data (padded to 32-bits)
    97  		size += int(12 + (uint32(len(tag.Buffer)+3) & 0xFFFFFFFC))
    98  	}
    99  
   100  	size += 4 // null tag
   101  
   102  	// Allow client to request bigger buffer for response
   103  	if size < message.MinSize {
   104  		size = message.MinSize
   105  	}
   106  
   107  	// Allocate temporary location-fixed buffer
   108  	addr, buf := mb.Region.Reserve(size, 16)
   109  	defer mb.Region.Release(addr)
   110  
   111  	binary.LittleEndian.PutUint32(buf[0:], uint32(size))
   112  	binary.LittleEndian.PutUint32(buf[4:], 0)
   113  
   114  	offset := 8
   115  	for _, tag := range message.Tags {
   116  		binary.LittleEndian.PutUint32(buf[offset:], tag.ID)
   117  		binary.LittleEndian.PutUint32(buf[offset+4:], uint32(len(tag.Buffer)))
   118  		binary.LittleEndian.PutUint32(buf[offset+8:], 0)
   119  		copy(buf[offset+12:], tag.Buffer)
   120  
   121  		offset += int(12 + (uint32(len(tag.Buffer)+3) & 0xFFFFFFFC))
   122  	}
   123  
   124  	// terminating null tag
   125  	binary.LittleEndian.PutUint32(buf[offset:], 0x0)
   126  
   127  	mb.exchangeMessage(channel, addr)
   128  
   129  	message.Tags = make([]MailboxTag, 0, len(message.Tags))
   130  	message.Code = binary.LittleEndian.Uint32(buf[4:])
   131  	offset = 8
   132  
   133  	for offset < len(buf) {
   134  		tag := MailboxTag{}
   135  		tag.ID = binary.LittleEndian.Uint32(buf[offset:])
   136  
   137  		// Terminating null tag
   138  		if tag.ID == 0 {
   139  			break
   140  		}
   141  
   142  		len := binary.LittleEndian.Uint32(buf[offset+4:])
   143  
   144  		if len > uint32(size-offset) {
   145  			panic("malformed mailbox response, over-sized tag")
   146  		}
   147  
   148  		tag.Buffer = make([]byte, len)
   149  		copy(tag.Buffer, buf[offset+12:])
   150  
   151  		// Move to next tag
   152  		offset += int(12 + (len+3)&0xFFFFFFFC)
   153  		message.Tags = append(message.Tags, tag)
   154  	}
   155  }
   156  
   157  func (mb *mailbox) exchangeMessage(channel int, addr uint32) {
   158  	if (addr & 0xF) != 0 {
   159  		panic("Mailbox message must be 16-byte aligned")
   160  	}
   161  
   162  	// For now, hold a global lock so only 1 outstanding mailbox
   163  	// message at any time.
   164  	mb.Mutex.Lock()
   165  	defer mb.Mutex.Unlock()
   166  
   167  	// Wait for space to send
   168  	for (reg.Read(peripheralBase+MAILBOX_STATUS_REG) & MAILBOX_FULL) != 0 {
   169  		runtime.Gosched()
   170  	}
   171  
   172  	// Send
   173  	reg.Write(peripheralBase+MAILBOX_WRITE_REG, uint32(channel&0xF)|uint32(addr&0xFFFFFFF0))
   174  
   175  	// Wait for response
   176  	for (reg.Read(peripheralBase+MAILBOX_STATUS_REG) & MAILBOX_EMPTY) != 0 {
   177  		runtime.Gosched()
   178  	}
   179  
   180  	// Read response
   181  	data := reg.Read(peripheralBase + MAILBOX_READ_REG)
   182  
   183  	// Ensure response corresponds to request (note response data over-writes request data)
   184  	if (data & 0xF) != uint32(channel&0xF) {
   185  		panic(fmt.Sprintf("overlapping messages, got response for channel %d, expecting %d", data&0xF, channel&0xF))
   186  	}
   187  	if (data & 0xFFFFFFF0) != (addr & 0xFFFFFFF0) {
   188  		panic(fmt.Sprintf("overlapping messages, got response for channel %d, expecting %d", data&0xFFFFFFF0, addr&0xFFFFFFF0))
   189  	}
   190  }