1/*
2 * Copyright (c) 2014, University of Washington.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <assert.h>
13#include <stdint.h>
14#include <string.h>
15#include <sys/types.h>
16#ifdef BARRELFISH
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/deferred.h>
19#include <barrelfish/waitset.h>
20#include <pci/pci.h>
21#include <lwip/inet.h>
22#else
23#include <arpa/inet.h>
24#include <pci/devids.h>
25#include <errors/errno.h>
26#include <sys/mman.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include "linux_defs.h"
32#endif
33#include <storage/vsic.h>
34#include <storage/vsa.h>
35
36#include "megaraid.h"
37
38struct megaraid_vsic {
39  struct megaraid_ctrl *ctrl;
40};
41
42static uint64_t htonll(uint64_t value)
43{
44    // The answer is 42
45    static const int num = 42;
46
47    // Check the endianness
48    if (*(const char *)&num == num)
49    {
50      const uint32_t high_part = htonl(value >> 32);
51      const uint32_t low_part = htonl(value & 0xFFFFFFFFLL);
52
53      return ((uint64_t)low_part << 32) | high_part;
54    } else {
55      return value;
56    }
57}
58
59static uintptr_t cmd_cnt = 0;
60
61static uint8_t *pmem_start = NULL;
62static uintptr_t paddr_start = 0;
63
64#define BUF_SIZE	(1 * 1024 * 1024)
65
66lpaddr_t v2p(void *ptr, size_t len)
67{
68  lpaddr_t paddr;
69
70  assert((uint8_t *)ptr >= pmem_start);
71  assert((uint8_t *)ptr < pmem_start + BUF_SIZE);
72  paddr = (uintptr_t)((uint8_t *)ptr - pmem_start);
73  paddr += paddr_start;
74  return paddr;
75}
76
77static void *alloc_map_frame(vregion_flags_t attr, size_t size,
78                             struct capref *retcap)
79{
80    struct capref frame;
81    errval_t r;
82
83    r = frame_alloc(&frame, size, NULL);
84    assert(err_is_ok(r));
85    void *va;
86    r = vspace_map_one_frame_attr(&va, size, frame, attr,
87                                  NULL, NULL);
88    if (err_is_fail(r)) {
89        DEBUG_ERR(r, "vspace_map_one_frame failed");
90        return NULL;
91    }
92
93    if (retcap != NULL) {
94        *retcap = frame;
95    }
96
97    return va;
98}
99
100void *user_alloc(size_t size, uintptr_t *paddr);
101void *user_alloc(size_t size, uintptr_t *paddr)
102{
103  struct capref cap = NULL_CAP;
104  void * va = alloc_map_frame(VREGION_FLAGS_READ_WRITE_NOCACHE,
105			      size, &cap);
106  assert(va != NULL);
107  struct frame_identity id;
108  errval_t err = frame_identify(cap, &id);
109  assert(err_is_ok(err));
110  *paddr = id.base;
111  return va;
112}
113
114static errval_t vsic_write(struct storage_vsic *vsic, struct storage_vsa *vsa,
115                           off_t offset, size_t size, void *buffer)
116{
117    assert(vsic != NULL);
118    assert(vsa != NULL);
119    assert(buffer != NULL);
120    /* struct megaraid_vsic *mydata = vsic->data; */
121
122    struct write16 {
123      uint8_t	opcode;
124      uint8_t	flags;
125      uint64_t	lba;
126      uint32_t	length;
127      uint8_t	group;
128      uint8_t	control;
129    } __attribute__ ((packed));
130
131    assert(offset % BLOCK_SIZE == 0);
132
133    memcpy(pmem_start, buffer, size);
134    buffer = pmem_start;
135
136    size = STORAGE_VSIC_ROUND(vsic, size);
137    /* assert(size % BLOCK_SIZE == 0); */
138
139    struct write16 write16_cmd = {
140      .opcode = 0x8a,
141      .flags = 0,
142      .lba = htonll(offset / BLOCK_SIZE),
143      .length = htonl(size / BLOCK_SIZE),
144      .group = 0,
145      .control = 0,
146    };
147
148    struct mrsas_mpt_cmd *cmd;
149    MRSAS_REQUEST_DESCRIPTOR_UNION *req_desc;
150
151    cmd = mrsas_get_mpt_cmd();
152    assert(cmd != NULL);
153
154    // Need virtual data addresses
155    cmd->flags = MRSAS_DIR_OUT;
156    cmd->length = size;
157    cmd->data = buffer;
158    cmd->ccb_ptr = (void *)cmd_cnt;	// Data pointer we can use
159
160    cmd_cnt++;
161
162    req_desc = mrsas_get_request_desc((cmd->index)-1);
163    assert(req_desc != NULL);
164    memset(req_desc, 0, sizeof(MRSAS_REQUEST_DESCRIPTOR_UNION));
165    cmd->request_desc = req_desc;
166
167    // command data block
168    memcpy(cmd->io_request->CDB.CDB32, &write16_cmd, 16);
169
170    // Build LDIO command
171    MRSAS_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
172    io_request->RaidContext.VirtualDiskTgtId = TARGET_DEVICE_ID;
173    io_request->RaidContext.status = 0;
174    io_request->RaidContext.exStatus = 0;
175    io_request->IoFlags = 16;	// CDB length in bytes
176    io_request->RaidContext.regLockFlags = 0;
177    io_request->RaidContext.timeoutValue = 0;
178    cmd->request_desc->SCSIIO.RequestFlags =
179      (MRSAS_REQ_DESCRIPT_FLAGS_LD_IO << MRSAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
180    if (io_request->RaidContext.regLockFlags == REGION_TYPE_UNUSED)
181      cmd->request_desc->SCSIIO.RequestFlags = (MRSAS_REQ_DESCRIPT_FLAGS_NO_LOCK << MRSAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
182    io_request->RaidContext.Type = MPI2_TYPE_CUDA;
183    io_request->RaidContext.regLockFlags |= (MR_RL_FLAGS_GRANT_DESTINATION_CPU0 | MR_RL_FLAGS_SEQ_NUM_ENABLE);
184    io_request->RaidContext.nseg = 0x1;
185    io_request->Function = MRSAS_MPI2_FUNCTION_LD_IO_REQUEST;
186    io_request->DevHandle = TARGET_DEVICE_ID;
187    io_request->DataLength = cmd->length;
188
189    pMpi25IeeeSgeChain64_t sgl_ptr;
190    sgl_ptr = (pMpi25IeeeSgeChain64_t)&io_request->SGL;
191    pMpi25IeeeSgeChain64_t sgl_ptr_end = sgl_ptr;
192    sgl_ptr_end += sc->max_sge_in_main_msg - 1;
193    sgl_ptr_end->Flags = 0;
194    sgl_ptr->Address = v2p(buffer, size);
195    sgl_ptr->Length = size;
196    sgl_ptr->Flags = IEEE_SGE_FLAGS_END_OF_LIST;
197    sgl_ptr++;
198    cmd->sge_count = 1;
199    io_request->RaidContext.numSGE = cmd->sge_count;
200
201    cmd->io_request->Control |= MPI2_SCSIIO_CONTROL_READ;
202    cmd->io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
203    cmd->io_request->SGLOffset0 = offsetof(MRSAS_RAID_SCSI_IO_REQUEST, SGL)/4;
204    cmd->io_request->SenseBufferLowAddress = cmd->sense_phys_addr;
205    cmd->io_request->SenseBufferLength = MRSAS_SCSI_SENSE_BUFFERSIZE;
206
207    req_desc->SCSIIO.SMID = cmd->index;
208
209    if (cmd->io_request->ChainOffset != 0 &&
210	cmd->io_request->ChainOffset != 0xF) {
211      DEBUG("megasas: The chain offset value is not "
212	     "correct : %x\n", cmd->io_request->ChainOffset);
213    }
214
215    DEBUG("Firing write cmd (outstanding %u)...\n", sc->fw_outstanding.val);
216
217    sc->fw_outstanding.val++;
218    mrsas_fire_cmd(req_desc->addr.u.low, req_desc->addr.u.high);
219
220    return SYS_ERR_OK;
221}
222
223static errval_t vsic_read(struct storage_vsic *vsic, struct storage_vsa *vsa,
224                          off_t offset, size_t size, void *buffer)
225{
226    assert(vsic != NULL);
227    assert(vsa != NULL);
228    assert(buffer != NULL);
229    /* struct megaraid_vsic *mydata = vsic->data; */
230
231    struct read16 {
232      uint8_t	opcode;
233      uint8_t	flags;
234      uint64_t	lba;
235      uint32_t	length;
236      uint8_t	group;
237      uint8_t	control;
238    } __attribute__ ((packed));
239
240    buffer = pmem_start;
241    assert(offset % BLOCK_SIZE == 0);
242    size = STORAGE_VSIC_ROUND(vsic, size);
243    /* assert(size % BLOCK_SIZE == 0); */
244
245    struct read16 read16_cmd = {
246      .opcode = 0x88,
247      .flags = 0,
248      .lba = htonll(offset / BLOCK_SIZE),
249      .length = htonl(size / BLOCK_SIZE),
250      .group = 0,
251      .control = 0,
252    };
253
254    struct mrsas_mpt_cmd *cmd;
255    MRSAS_REQUEST_DESCRIPTOR_UNION *req_desc;
256
257    cmd = mrsas_get_mpt_cmd();
258    assert(cmd != NULL);
259    cmd->flags = MRSAS_DIR_IN;
260
261    // Need virtual data addresses
262    cmd->length = size;
263    cmd->data = buffer;
264    cmd->ccb_ptr = NULL;	// Data pointer we can use
265
266    req_desc = mrsas_get_request_desc((cmd->index)-1);
267    assert(req_desc != NULL);
268    memset(req_desc, 0, sizeof(MRSAS_REQUEST_DESCRIPTOR_UNION));
269    cmd->request_desc = req_desc;
270
271    // command data block
272    memcpy(cmd->io_request->CDB.CDB32, &read16_cmd, 16);
273
274    // Build LDIO command
275    MRSAS_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
276    io_request->RaidContext.VirtualDiskTgtId = TARGET_DEVICE_ID;
277    io_request->RaidContext.status = 0;
278    io_request->RaidContext.exStatus = 0;
279    io_request->IoFlags = 16;	// CDB length in bytes
280    io_request->RaidContext.regLockFlags = 0;
281    io_request->RaidContext.timeoutValue = 0;
282    cmd->request_desc->SCSIIO.RequestFlags =
283      (MRSAS_REQ_DESCRIPT_FLAGS_LD_IO << MRSAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
284    if (io_request->RaidContext.regLockFlags == REGION_TYPE_UNUSED)
285      cmd->request_desc->SCSIIO.RequestFlags = (MRSAS_REQ_DESCRIPT_FLAGS_NO_LOCK << MRSAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
286    io_request->RaidContext.Type = MPI2_TYPE_CUDA;
287    io_request->RaidContext.regLockFlags |= (MR_RL_FLAGS_GRANT_DESTINATION_CPU0 | MR_RL_FLAGS_SEQ_NUM_ENABLE);
288    io_request->RaidContext.nseg = 0x1;
289    io_request->Function = MRSAS_MPI2_FUNCTION_LD_IO_REQUEST;
290    io_request->DevHandle = TARGET_DEVICE_ID;
291    io_request->DataLength = cmd->length;
292
293    pMpi25IeeeSgeChain64_t sgl_ptr;
294    sgl_ptr = (pMpi25IeeeSgeChain64_t)&io_request->SGL;
295    pMpi25IeeeSgeChain64_t sgl_ptr_end = sgl_ptr;
296    sgl_ptr_end += sc->max_sge_in_main_msg - 1;
297    sgl_ptr_end->Flags = 0;
298    sgl_ptr->Address = v2p(buffer, size);
299    sgl_ptr->Length = size;
300    sgl_ptr->Flags = IEEE_SGE_FLAGS_END_OF_LIST;
301    sgl_ptr++;
302    cmd->sge_count = 1;
303    io_request->RaidContext.numSGE = cmd->sge_count;
304
305    cmd->io_request->Control |= MPI2_SCSIIO_CONTROL_READ;
306    cmd->io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
307    cmd->io_request->SGLOffset0 = offsetof(MRSAS_RAID_SCSI_IO_REQUEST, SGL)/4;
308    cmd->io_request->SenseBufferLowAddress = cmd->sense_phys_addr;
309    cmd->io_request->SenseBufferLength = MRSAS_SCSI_SENSE_BUFFERSIZE;
310
311    req_desc->SCSIIO.SMID = cmd->index;
312
313    if (cmd->io_request->ChainOffset != 0 &&
314	cmd->io_request->ChainOffset != 0xF) {
315      DEBUG("megasas: The chain offset value is not "
316	     "correct : %x\n", cmd->io_request->ChainOffset);
317    }
318
319    DEBUG("Firing read cmd...\n");
320
321    sc->fw_outstanding.val++;
322    mrsas_fire_cmd(req_desc->addr.u.low, req_desc->addr.u.high);
323
324    return SYS_ERR_OK;
325}
326
327static errval_t vsic_flush(struct storage_vsic *vsic, struct storage_vsa *vsa)
328{
329    assert(vsic != NULL);
330    assert(vsa != NULL);
331    /* struct megaraid_vsic *mydata = vsic->data; */
332
333    return SYS_ERR_OK;
334}
335
336static errval_t vsic_flush2(struct storage_vsic *vsic, struct storage_vsa *vsa,
337                            void *handle)
338{
339    assert(vsic != NULL);
340    assert(vsa != NULL);
341    /* struct megaraid_vsic *mydata = vsic->data; */
342
343    return SYS_ERR_OK;
344}
345
346static errval_t vsic_wait(struct storage_vsic *vsic)
347{
348    assert(vsic != NULL);
349    /* struct megaraid_vsic *mydata = vsic->data; */
350
351    poll_mode = false;
352
353    while(sc->fw_outstanding.val > 0) {
354      /* DEBUG("Outstanding cmds = %u\n", sc->fw_outstanding.val); */
355      mrsas_complete_cmd();
356    }
357
358    return SYS_ERR_OK;
359}
360
361static errval_t vsic_poll(struct storage_vsic *vsic, void **handle)
362{
363    assert(vsic != NULL);
364    /* struct megaraid_vsic *mydata = vsic->data; */
365
366    if(sc->fw_outstanding.val > 0) {
367        DEBUG("polling: Outstanding cmds = %u\n", sc->fw_outstanding.val);
368        poll_mode = true;
369        mrsas_complete_cmd();
370        *handle = (void *)1234;
371    } else {
372        return FLOUNDER_ERR_TX_BUSY;
373    }
374
375    return SYS_ERR_OK;
376}
377
378static struct storage_vsic_ops megaraid_ops = {
379    .write = vsic_write,
380    .read = vsic_read,
381    .flush = vsic_flush,
382    .wait = vsic_wait,
383    .flush2 = vsic_flush2,
384    .poll = vsic_poll,
385};
386
387errval_t storage_vsic_driver_init(int argc, const char **argv,
388				  struct storage_vsic *vsic)
389{
390    assert(vsic != NULL);
391    struct megaraid_vsic *mydata = malloc(sizeof(struct megaraid_vsic));
392    assert(mydata != NULL);
393    memset(mydata, 0, sizeof(struct megaraid_vsic));
394
395    // Init VSIC data structures
396    vsic->ops = megaraid_ops;
397    vsic->data = mydata;
398    vsic->blocksize = BLOCK_SIZE;	// XXX: Determine from drive?
399
400    megaraid_driver_init(argc, argv);
401
402    pmem_start = user_alloc(BUF_SIZE, &paddr_start);
403    assert(pmem_start != NULL);
404
405    return SYS_ERR_OK;
406}
407
408errval_t storage_vsa_acquire(struct storage_vsa *vsa, const char *name,
409			     size_t size)
410{
411    // XXX: Always return empty VSA of fixed size
412    return SYS_ERR_OK;
413}
414
415errval_t storage_vsa_resize(struct storage_vsa *vsa, size_t size)
416{
417    assert("NYI");
418    return SYS_ERR_OK;
419}
420