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 }