1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <fcntl.h>
19#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/ioctl.h>
24#include <unistd.h>
25
26#include <linux/major.h>
27#include <linux/mmc/ioctl.h>
28
29#include "ipc.h"
30#include "log.h"
31#include "rpmb.h"
32#include "storage.h"
33
34#define MMC_READ_MULTIPLE_BLOCK 18
35#define MMC_WRITE_MULTIPLE_BLOCK 25
36#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
37
38#define MMC_RSP_PRESENT (1 << 0)
39#define MMC_RSP_CRC (1 << 2)
40#define MMC_RSP_OPCODE (1 << 4)
41#define MMC_CMD_ADTC (1 << 5)
42#define MMC_RSP_SPI_S1 (1 << 7)
43#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
44#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
45
46#define MMC_WRITE_FLAG_R 0
47#define MMC_WRITE_FLAG_W 1
48#define MMC_WRITE_FLAG_RELW (MMC_WRITE_FLAG_W | MMC_RELIABLE_WRITE_FLAG)
49
50#define MMC_BLOCK_SIZE 512
51
52static int rpmb_fd = -1;
53static uint8_t read_buf[4096];
54
55#ifdef RPMB_DEBUG
56
57static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
58{
59    size_t i;
60
61    printf("%s @%p [%zu]", prefix, buf, size);
62    for (i = 0; i < size; i++) {
63        if (i && i % 32 == 0)
64            printf("\n%*s", (int) strlen(prefix), "");
65        printf(" %02x", buf[i]);
66    }
67    printf("\n");
68    fflush(stdout);
69}
70
71#endif
72
73
74int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
75{
76    int rc;
77    struct {
78        struct mmc_ioc_multi_cmd multi;
79        struct mmc_ioc_cmd cmd_buf[3];
80    } mmc = {};
81    struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
82    const struct storage_rpmb_send_req *req = r;
83
84    if (req_len < sizeof(*req)) {
85        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
86              req_len, sizeof(*req));
87        msg->result = STORAGE_ERR_NOT_VALID;
88        goto err_response;
89    }
90
91    size_t expected_len =
92            sizeof(*req) + req->reliable_write_size + req->write_size;
93    if (req_len != expected_len) {
94        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
95              req_len, expected_len);
96        msg->result = STORAGE_ERR_NOT_VALID;
97        goto err_response;
98    }
99
100    const uint8_t *write_buf = req->payload;
101    if (req->reliable_write_size) {
102        if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
103            ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
104            msg->result = STORAGE_ERR_NOT_VALID;
105            goto err_response;
106        }
107
108        cmd->write_flag = MMC_WRITE_FLAG_RELW;
109        cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
110        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
111        cmd->blksz = MMC_BLOCK_SIZE;
112        cmd->blocks = req->reliable_write_size / MMC_BLOCK_SIZE;
113        mmc_ioc_cmd_set_data((*cmd), write_buf);
114#ifdef RPMB_DEBUG
115        ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
116        print_buf("request: ", write_buf, req->reliable_write_size);
117#endif
118        write_buf += req->reliable_write_size;
119        mmc.multi.num_of_cmds++;
120        cmd++;
121    }
122
123    if (req->write_size) {
124        if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
125            ALOGW("invalid write size %u\n", req->write_size);
126            msg->result = STORAGE_ERR_NOT_VALID;
127            goto err_response;
128        }
129
130        cmd->write_flag = MMC_WRITE_FLAG_W;
131        cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
132        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
133        cmd->blksz = MMC_BLOCK_SIZE;
134        cmd->blocks = req->write_size / MMC_BLOCK_SIZE;
135        mmc_ioc_cmd_set_data((*cmd), write_buf);
136#ifdef RPMB_DEBUG
137        ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
138        print_buf("request: ", write_buf, req->write_size);
139#endif
140        write_buf += req->write_size;
141        mmc.multi.num_of_cmds++;
142        cmd++;
143    }
144
145    if (req->read_size) {
146        if (req->read_size % MMC_BLOCK_SIZE != 0 ||
147            req->read_size > sizeof(read_buf)) {
148            ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
149            msg->result = STORAGE_ERR_NOT_VALID;
150            goto err_response;
151        }
152
153        cmd->write_flag = MMC_WRITE_FLAG_R;
154        cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
155        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
156        cmd->blksz = MMC_BLOCK_SIZE;
157        cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
158        mmc_ioc_cmd_set_data((*cmd), read_buf);
159#ifdef RPMB_DEBUG
160        ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
161#endif
162        mmc.multi.num_of_cmds++;
163        cmd++;
164    }
165
166    rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
167    if (rc < 0) {
168        ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
169        msg->result = STORAGE_ERR_GENERIC;
170        goto err_response;
171    }
172#ifdef RPMB_DEBUG
173    if (req->read_size)
174        print_buf("response: ", read_buf, req->read_size);
175#endif
176
177    if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
178        /*
179         * Nothing todo for post msg commit request as MMC_IOC_MULTI_CMD
180         * is fully synchronous in this implementation.
181         */
182    }
183
184    msg->result = STORAGE_NO_ERROR;
185    return ipc_respond(msg, read_buf, req->read_size);
186
187err_response:
188    return ipc_respond(msg, NULL, 0);
189}
190
191
192int rpmb_open(const char *rpmb_devname)
193{
194    int rc;
195
196    rc = open(rpmb_devname, O_RDWR, 0);
197    if (rc < 0) {
198        ALOGE("unable (%d) to open rpmb device '%s': %s\n",
199              errno, rpmb_devname, strerror(errno));
200        return rc;
201    }
202    rpmb_fd = rc;
203    return 0;
204}
205
206void rpmb_close(void)
207{
208    close(rpmb_fd);
209    rpmb_fd = -1;
210}
211
212