1 /* 2 * A dataspace server exposing regions of a MMC/SD card. 3 * 4 * Copyright (C) 2020, 2021, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/cpm-generic.h> 23 #include <l4/devices/dma-generic.h> 24 #include <l4/devices/memory.h> 25 #include <l4/devices/msc-generic.h> 26 27 #include <l4/re/c/util/cap_alloc.h> 28 #include <l4/sys/debugger.h> 29 #include <l4/sys/err.h> 30 #include <l4/sys/factory.h> 31 #include <l4/sys/icu.h> 32 #include <l4/sys/ipc.h> 33 #include <l4/sys/irq.h> 34 35 #include <ipc/thread.h> 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include <mem/memory_incremental.h> 42 #include <fsserver/page_queue_shared.h> 43 #include <fsserver/pages.h> 44 #include <resource/resource_server.h> 45 #include <systypes/env.h> 46 47 #include "msc_region_opener.h" 48 #include "msc_region_operations.h" 49 50 51 52 /* Common configuration. */ 53 54 static l4_cap_idx_t icucap; 55 56 57 58 /* Device and resource discovery. */ 59 60 static long item_in_range(long start, long end, long index) 61 { 62 if (start < end) 63 return start + index; 64 else if (start > end) 65 return start - index; 66 else 67 return start; 68 } 69 70 71 72 /* Common functions. */ 73 74 static int init_irq(int num, l4_cap_idx_t irq, l4_uint32_t start, l4_uint32_t end) 75 { 76 /* Create interrupt object. */ 77 78 long err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq)); 79 80 if (err) 81 { 82 printf("Could not create IRQ object: %ld\n", err); 83 return 1; 84 } 85 86 /* Bind interrupt objects to IRQ numbers. */ 87 88 err = l4_error(l4_icu_bind(icucap, 89 item_in_range(start, end, num), 90 irq)); 91 92 if (err) 93 { 94 printf("Could not bind IRQ to the ICU: %ld\n", err); 95 return 1; 96 } 97 98 return 0; 99 } 100 101 102 103 /* Peripheral resources. */ 104 105 static Cpm_chip *cpm; 106 static Dma_chip *dma; 107 static Msc_chip *msc; 108 109 /* Obtain an abstraction for the memory card. */ 110 111 static MscRegionOperations *get_msc_region_operations(const char *machine, 112 int msc_channel_num, 113 int dma_channel_num, 114 int card) 115 { 116 char resource[strlen(machine) + 8]; 117 l4_addr_t cpm_base = 0, cpm_base_end = 0; 118 l4_addr_t dma_base = 0, dma_base_end = 0; 119 l4_addr_t msc_base = 0, msc_base_end = 0; 120 l4_addr_t msc_phys_base = 0, msc_phys_base_end = 0; 121 l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; 122 l4_uint32_t msc_irq_start = 0, msc_irq_end = 0; 123 124 icucap = l4re_env_get_cap("icu"); 125 126 /* Obtain resource details describing I/O memory. */ 127 128 sprintf(resource, "%s-cpm", machine); 129 130 if (get_memory(resource, &cpm_base, &cpm_base_end) < 0) 131 return NULL; 132 133 cpm = new_cpm_chip(machine, cpm_base); 134 135 sprintf(resource, "%s-dma", machine); 136 137 if (get_memory(resource, &dma_base, &dma_base_end) < 0) 138 return NULL; 139 140 dma = new_dma_chip(machine, dma_base, dma_base_end, cpm); 141 142 if (get_irq(resource, &dma_irq_start, &dma_irq_end) < 0) 143 return NULL; 144 145 l4_cap_idx_t dma_irq = l4re_util_cap_alloc(); 146 147 if (init_irq(0, dma_irq, dma_irq_start, dma_irq_end)) 148 return NULL; 149 150 dma->enable(); 151 152 sprintf(resource, "%s-msc", machine); 153 154 if (get_memory_complete(resource, &msc_base, &msc_base_end, 155 &msc_phys_base, &msc_phys_base_end) < 0) 156 return NULL; 157 158 msc = new_msc_chip(machine, msc_phys_base, msc_base, msc_base_end, cpm); 159 160 if (get_irq(resource, &msc_irq_start, &msc_irq_end) < 0) 161 return NULL; 162 163 l4_cap_idx_t msc_irq = l4re_util_cap_alloc(); 164 165 if (init_irq(msc_channel_num, msc_irq, msc_irq_start, msc_irq_end)) 166 return NULL; 167 168 Dma_channel *dma_channel = dma->get_channel(dma_channel_num, dma_irq); 169 Msc_channel *msc_channel = msc->get_channel(msc_channel_num, msc_irq, dma_channel); 170 171 msc_channel->enable(); 172 173 if (card >= msc_channel->num_cards()) 174 return NULL; 175 176 struct dma_region region; 177 178 if (get_dma_region(512, 12, ®ion)) 179 return NULL; 180 181 return new MscRegionOperations(msc_channel, card, region); 182 } 183 184 185 186 /* Default number of pages for files. */ 187 188 const unsigned int MEMORY_PAGES = 20; 189 190 191 192 /* Server program. */ 193 194 int main(int argc, char *argv[]) 195 { 196 l4_debugger_set_object_name(l4re_env()->main_thread, "block_server"); 197 long err; 198 199 /* Introduce concurrency control. */ 200 201 err = ipc_thread_init(); 202 203 if (err) 204 { 205 printf("Initialisation error: %s\n", l4sys_errtostr(err)); 206 return 1; 207 } 208 209 if (argc < 5) 210 { 211 printf("Need the following:\n\n" \ 212 "A machine indicator such as jz4780 or x1600.\n" \ 213 "An MSC channel/peripheral number.\n" \ 214 "A DMA channel number.\n" \ 215 "A card number.\n\n" \ 216 "A number of memory pages can be indicated for the use of the " \ 217 "server.\n\n" \ 218 "A named capability from the environment can be specified.\n"); 219 return 1; 220 } 221 222 char *machine = argv[1]; 223 int msc_channel_num = atoi(argv[2]); 224 int dma_channel_num = atoi(argv[3]); 225 int card = atoi(argv[4]); 226 227 unsigned int memory_pages = MEMORY_PAGES; 228 229 if (argc > 5) 230 memory_pages = atoi(argv[5]); 231 232 const char *server_name = (argc > 6) ? argv[6] : ENV_FILESYSTEM_SERVER_NAME; 233 234 /* Obtain a DMA space for associating allocated memory with physical 235 addresses. */ 236 237 l4_cap_idx_t dma_space; 238 239 err = get_dma_space(&dma_space); 240 241 if (err) 242 { 243 printf("Could not obtain DMA space: %s\n", l4sys_errtostr(err)); 244 return 1; 245 } 246 247 MscRegionOperations *ops = get_msc_region_operations(machine, 248 msc_channel_num, 249 dma_channel_num, 250 card); 251 252 if (ops == NULL) 253 { 254 printf("Could not access memory card peripheral.\n"); 255 return 1; 256 } 257 258 /* Some memory plus infrastructure. */ 259 260 MemoryIncremental mem(memory_pages, PAGE_SIZE, dma_space); 261 PageQueueShared queue; 262 Pages pages(&mem, &queue); 263 ResourceRegistry registry(&pages); 264 MscRegionOpener opener(®istry, ops); 265 266 /* Register a server associating it with the given object. */ 267 268 ResourceServer server(&opener); 269 server.bind(server_name); 270 271 printf("Starting server using %d pages...\n", memory_pages); 272 server.start(); 273 274 return 0; 275 } 276 277 // vim: tabstop=2 expandtab shiftwidth=2