1288522Sgrehan/*- 2288522Sgrehan * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org> 3288522Sgrehan * All rights reserved. 4288522Sgrehan * 5288522Sgrehan * Redistribution and use in source and binary forms, with or without 6288522Sgrehan * modification, are permitted provided that the following conditions 7288522Sgrehan * are met: 8288522Sgrehan * 1. Redistributions of source code must retain the above copyright 9288522Sgrehan * notice, this list of conditions and the following disclaimer. 10288522Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11288522Sgrehan * notice, this list of conditions and the following disclaimer in the 12288522Sgrehan * documentation and/or other materials provided with the distribution. 13288522Sgrehan * 14288522Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15288522Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16288522Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17288522Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18288522Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19288522Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20288522Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21288522Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22288522Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23288522Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24288522Sgrehan * SUCH DAMAGE. 25288522Sgrehan * 26288522Sgrehan * $FreeBSD: stable/10/usr.sbin/bhyve/fwctl.c 341607 2018-12-05 21:51:39Z emaste $ 27288522Sgrehan */ 28288522Sgrehan 29288522Sgrehan/* 30288522Sgrehan * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does, 31288522Sgrehan * but with a request/response messaging protocol. 32288522Sgrehan */ 33288522Sgrehan#include <sys/cdefs.h> 34288522Sgrehan__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/fwctl.c 341607 2018-12-05 21:51:39Z emaste $"); 35288522Sgrehan 36288522Sgrehan#include <sys/param.h> 37288522Sgrehan#include <sys/types.h> 38288522Sgrehan#include <sys/errno.h> 39288522Sgrehan#include <sys/uio.h> 40288522Sgrehan 41288522Sgrehan#include <assert.h> 42288522Sgrehan#include <stdio.h> 43288522Sgrehan#include <stdlib.h> 44288522Sgrehan#include <string.h> 45288522Sgrehan 46288522Sgrehan#include "bhyverun.h" 47288522Sgrehan#include "inout.h" 48288522Sgrehan#include "fwctl.h" 49288522Sgrehan 50288522Sgrehan/* 51288522Sgrehan * Messaging protocol base operations 52288522Sgrehan */ 53288522Sgrehan#define OP_NULL 1 54288522Sgrehan#define OP_ECHO 2 55288522Sgrehan#define OP_GET 3 56288522Sgrehan#define OP_GET_LEN 4 57288522Sgrehan#define OP_SET 5 58288522Sgrehan#define OP_MAX OP_SET 59288522Sgrehan 60288522Sgrehan/* I/O ports */ 61288522Sgrehan#define FWCTL_OUT 0x510 62288522Sgrehan#define FWCTL_IN 0x511 63288522Sgrehan 64288522Sgrehan/* 65288522Sgrehan * Back-end state-machine 66288522Sgrehan */ 67288522Sgrehanenum state { 68288522Sgrehan DORMANT, 69288522Sgrehan IDENT_WAIT, 70288522Sgrehan IDENT_SEND, 71288522Sgrehan REQ, 72288522Sgrehan RESP 73288522Sgrehan} be_state = DORMANT; 74288522Sgrehan 75288522Sgrehanstatic uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 76288522Sgrehanstatic u_int ident_idx; 77288522Sgrehan 78288522Sgrehanstruct op_info { 79288522Sgrehan int op; 80341607Semaste int (*op_start)(uint32_t len); 81341607Semaste void (*op_data)(uint32_t data, uint32_t len); 82288522Sgrehan int (*op_result)(struct iovec **data); 83288522Sgrehan void (*op_done)(struct iovec *data); 84288522Sgrehan}; 85288522Sgrehanstatic struct op_info *ops[OP_MAX+1]; 86288522Sgrehan 87288522Sgrehan/* Return 0-padded uint32_t */ 88288522Sgrehanstatic uint32_t 89288522Sgrehanfwctl_send_rest(uint32_t *data, size_t len) 90288522Sgrehan{ 91288522Sgrehan union { 92288522Sgrehan uint8_t c[4]; 93288522Sgrehan uint32_t w; 94288522Sgrehan } u; 95288522Sgrehan uint8_t *cdata; 96288522Sgrehan int i; 97288522Sgrehan 98288522Sgrehan cdata = (uint8_t *) data; 99288522Sgrehan u.w = 0; 100288522Sgrehan 101288522Sgrehan for (i = 0, u.w = 0; i < len; i++) 102288522Sgrehan u.c[i] = *cdata++; 103288522Sgrehan 104288522Sgrehan return (u.w); 105288522Sgrehan} 106288522Sgrehan 107288522Sgrehan/* 108288522Sgrehan * error op dummy proto - drop all data sent and return an error 109288522Sgrehan*/ 110288522Sgrehanstatic int errop_code; 111288522Sgrehan 112288522Sgrehanstatic void 113288522Sgrehanerrop_set(int err) 114288522Sgrehan{ 115288522Sgrehan 116288522Sgrehan errop_code = err; 117288522Sgrehan} 118288522Sgrehan 119288522Sgrehanstatic int 120341607Semasteerrop_start(uint32_t len) 121288522Sgrehan{ 122288522Sgrehan errop_code = ENOENT; 123288522Sgrehan 124288522Sgrehan /* accept any length */ 125288522Sgrehan return (errop_code); 126288522Sgrehan} 127288522Sgrehan 128288522Sgrehanstatic void 129341607Semasteerrop_data(uint32_t data, uint32_t len) 130288522Sgrehan{ 131288522Sgrehan 132288522Sgrehan /* ignore */ 133288522Sgrehan} 134288522Sgrehan 135288522Sgrehanstatic int 136288522Sgrehanerrop_result(struct iovec **data) 137288522Sgrehan{ 138288522Sgrehan 139288522Sgrehan /* no data to send back; always successful */ 140288522Sgrehan *data = NULL; 141288522Sgrehan return (errop_code); 142288522Sgrehan} 143288522Sgrehan 144288522Sgrehanstatic void 145288522Sgrehanerrop_done(struct iovec *data) 146288522Sgrehan{ 147288522Sgrehan 148288522Sgrehan /* assert data is NULL */ 149288522Sgrehan} 150288522Sgrehan 151288522Sgrehanstatic struct op_info errop_info = { 152288522Sgrehan .op_start = errop_start, 153288522Sgrehan .op_data = errop_data, 154288522Sgrehan .op_result = errop_result, 155288522Sgrehan .op_done = errop_done 156288522Sgrehan}; 157288522Sgrehan 158288522Sgrehan/* OID search */ 159288522SgrehanSET_DECLARE(ctl_set, struct ctl); 160288522Sgrehan 161288522SgrehanCTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 162288522Sgrehan 163288522Sgrehanstatic struct ctl * 164288522Sgrehanctl_locate(const char *str, int maxlen) 165288522Sgrehan{ 166288522Sgrehan struct ctl *cp, **cpp; 167288522Sgrehan 168288522Sgrehan SET_FOREACH(cpp, ctl_set) { 169288522Sgrehan cp = *cpp; 170288522Sgrehan if (!strncmp(str, cp->c_oid, maxlen)) 171288522Sgrehan return (cp); 172288522Sgrehan } 173288522Sgrehan 174288522Sgrehan return (NULL); 175288522Sgrehan} 176288522Sgrehan 177288522Sgrehan/* uefi-sysctl get-len */ 178288522Sgrehan#define FGET_STRSZ 80 179288522Sgrehanstatic struct iovec fget_biov[2]; 180288522Sgrehanstatic char fget_str[FGET_STRSZ]; 181288522Sgrehanstatic struct { 182288522Sgrehan size_t f_sz; 183288522Sgrehan uint32_t f_data[1024]; 184288522Sgrehan} fget_buf; 185288522Sgrehanstatic int fget_cnt; 186288522Sgrehanstatic size_t fget_size; 187288522Sgrehan 188288522Sgrehanstatic int 189341607Semastefget_start(uint32_t len) 190288522Sgrehan{ 191288522Sgrehan 192288522Sgrehan if (len > FGET_STRSZ) 193288522Sgrehan return(E2BIG); 194288522Sgrehan 195288522Sgrehan fget_cnt = 0; 196288522Sgrehan 197288522Sgrehan return (0); 198288522Sgrehan} 199288522Sgrehan 200288522Sgrehanstatic void 201341607Semastefget_data(uint32_t data, uint32_t len) 202288522Sgrehan{ 203288522Sgrehan 204288522Sgrehan *((uint32_t *) &fget_str[fget_cnt]) = data; 205288522Sgrehan fget_cnt += sizeof(uint32_t); 206288522Sgrehan} 207288522Sgrehan 208288522Sgrehanstatic int 209288522Sgrehanfget_result(struct iovec **data, int val) 210288522Sgrehan{ 211288522Sgrehan struct ctl *cp; 212288522Sgrehan int err; 213288522Sgrehan 214288522Sgrehan err = 0; 215288522Sgrehan 216288522Sgrehan /* Locate the OID */ 217288522Sgrehan cp = ctl_locate(fget_str, fget_cnt); 218288522Sgrehan if (cp == NULL) { 219288522Sgrehan *data = NULL; 220288522Sgrehan err = ENOENT; 221288522Sgrehan } else { 222288522Sgrehan if (val) { 223288522Sgrehan /* For now, copy the len/data into a buffer */ 224288522Sgrehan memset(&fget_buf, 0, sizeof(fget_buf)); 225288522Sgrehan fget_buf.f_sz = cp->c_len; 226288522Sgrehan memcpy(fget_buf.f_data, cp->c_data, cp->c_len); 227288522Sgrehan fget_biov[0].iov_base = (char *)&fget_buf; 228288522Sgrehan fget_biov[0].iov_len = sizeof(fget_buf.f_sz) + 229288522Sgrehan cp->c_len; 230288522Sgrehan } else { 231288522Sgrehan fget_size = cp->c_len; 232288522Sgrehan fget_biov[0].iov_base = (char *)&fget_size; 233288522Sgrehan fget_biov[0].iov_len = sizeof(fget_size); 234288522Sgrehan } 235288522Sgrehan 236288522Sgrehan fget_biov[1].iov_base = NULL; 237288522Sgrehan fget_biov[1].iov_len = 0; 238288522Sgrehan *data = fget_biov; 239288522Sgrehan } 240288522Sgrehan 241288522Sgrehan return (err); 242288522Sgrehan} 243288522Sgrehan 244288522Sgrehanstatic void 245288522Sgrehanfget_done(struct iovec *data) 246288522Sgrehan{ 247288522Sgrehan 248288522Sgrehan /* nothing needs to be freed */ 249288522Sgrehan} 250288522Sgrehan 251288522Sgrehanstatic int 252288522Sgrehanfget_len_result(struct iovec **data) 253288522Sgrehan{ 254288522Sgrehan return (fget_result(data, 0)); 255288522Sgrehan} 256288522Sgrehan 257288522Sgrehanstatic int 258288522Sgrehanfget_val_result(struct iovec **data) 259288522Sgrehan{ 260288522Sgrehan return (fget_result(data, 1)); 261288522Sgrehan} 262288522Sgrehan 263288522Sgrehanstatic struct op_info fgetlen_info = { 264288522Sgrehan .op_start = fget_start, 265288522Sgrehan .op_data = fget_data, 266288522Sgrehan .op_result = fget_len_result, 267288522Sgrehan .op_done = fget_done 268288522Sgrehan}; 269288522Sgrehan 270288522Sgrehanstatic struct op_info fgetval_info = { 271288522Sgrehan .op_start = fget_start, 272288522Sgrehan .op_data = fget_data, 273288522Sgrehan .op_result = fget_val_result, 274288522Sgrehan .op_done = fget_done 275288522Sgrehan}; 276288522Sgrehan 277288522Sgrehanstatic struct req_info { 278288522Sgrehan int req_error; 279288522Sgrehan u_int req_count; 280288522Sgrehan uint32_t req_size; 281288522Sgrehan uint32_t req_type; 282288522Sgrehan uint32_t req_txid; 283288522Sgrehan struct op_info *req_op; 284288522Sgrehan int resp_error; 285288522Sgrehan int resp_count; 286341607Semaste size_t resp_size; 287341607Semaste size_t resp_off; 288288522Sgrehan struct iovec *resp_biov; 289288522Sgrehan} rinfo; 290288522Sgrehan 291288522Sgrehanstatic void 292288522Sgrehanfwctl_response_done(void) 293288522Sgrehan{ 294288522Sgrehan 295288522Sgrehan (*rinfo.req_op->op_done)(rinfo.resp_biov); 296288522Sgrehan 297288522Sgrehan /* reinit the req data struct */ 298288522Sgrehan memset(&rinfo, 0, sizeof(rinfo)); 299288522Sgrehan} 300288522Sgrehan 301288522Sgrehanstatic void 302288522Sgrehanfwctl_request_done(void) 303288522Sgrehan{ 304288522Sgrehan 305288522Sgrehan rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov); 306288522Sgrehan 307288522Sgrehan /* XXX only a single vector supported at the moment */ 308288522Sgrehan rinfo.resp_off = 0; 309288522Sgrehan if (rinfo.resp_biov == NULL) { 310288522Sgrehan rinfo.resp_size = 0; 311288522Sgrehan } else { 312288522Sgrehan rinfo.resp_size = rinfo.resp_biov[0].iov_len; 313288522Sgrehan } 314288522Sgrehan} 315288522Sgrehan 316288522Sgrehanstatic int 317288522Sgrehanfwctl_request_start(void) 318288522Sgrehan{ 319288522Sgrehan int err; 320288522Sgrehan 321288522Sgrehan /* Data size doesn't include header */ 322288522Sgrehan rinfo.req_size -= 12; 323288522Sgrehan 324288522Sgrehan rinfo.req_op = &errop_info; 325288522Sgrehan if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL) 326288522Sgrehan rinfo.req_op = ops[rinfo.req_type]; 327288522Sgrehan 328288522Sgrehan err = (*rinfo.req_op->op_start)(rinfo.req_size); 329288522Sgrehan 330288522Sgrehan if (err) { 331288522Sgrehan errop_set(err); 332288522Sgrehan rinfo.req_op = &errop_info; 333288522Sgrehan } 334288522Sgrehan 335288522Sgrehan /* Catch case of zero-length message here */ 336288522Sgrehan if (rinfo.req_size == 0) { 337288522Sgrehan fwctl_request_done(); 338288522Sgrehan return (1); 339288522Sgrehan } 340288522Sgrehan 341288522Sgrehan return (0); 342288522Sgrehan} 343288522Sgrehan 344288522Sgrehanstatic int 345288522Sgrehanfwctl_request_data(uint32_t value) 346288522Sgrehan{ 347288522Sgrehan 348288522Sgrehan /* Make sure remaining size is >= 0 */ 349341607Semaste if (rinfo.req_size <= sizeof(uint32_t)) 350341607Semaste rinfo.req_size = 0; 351341607Semaste else 352341607Semaste rinfo.req_size -= sizeof(uint32_t); 353288522Sgrehan 354341607Semaste (*rinfo.req_op->op_data)(value, rinfo.req_size); 355288522Sgrehan 356288522Sgrehan if (rinfo.req_size < sizeof(uint32_t)) { 357288522Sgrehan fwctl_request_done(); 358288522Sgrehan return (1); 359288522Sgrehan } 360288522Sgrehan 361288522Sgrehan return (0); 362288522Sgrehan} 363288522Sgrehan 364288522Sgrehanstatic int 365288522Sgrehanfwctl_request(uint32_t value) 366288522Sgrehan{ 367288522Sgrehan 368288522Sgrehan int ret; 369288522Sgrehan 370288522Sgrehan ret = 0; 371288522Sgrehan 372288522Sgrehan switch (rinfo.req_count) { 373288522Sgrehan case 0: 374288522Sgrehan /* Verify size */ 375288522Sgrehan if (value < 12) { 376288522Sgrehan printf("msg size error"); 377288522Sgrehan exit(1); 378288522Sgrehan } 379288522Sgrehan rinfo.req_size = value; 380288522Sgrehan rinfo.req_count = 1; 381288522Sgrehan break; 382288522Sgrehan case 1: 383288522Sgrehan rinfo.req_type = value; 384288522Sgrehan rinfo.req_count++; 385288522Sgrehan break; 386288522Sgrehan case 2: 387288522Sgrehan rinfo.req_txid = value; 388288522Sgrehan rinfo.req_count++; 389288522Sgrehan ret = fwctl_request_start(); 390288522Sgrehan break; 391288522Sgrehan default: 392288522Sgrehan ret = fwctl_request_data(value); 393288522Sgrehan break; 394288522Sgrehan } 395288522Sgrehan 396288522Sgrehan return (ret); 397288522Sgrehan} 398288522Sgrehan 399288522Sgrehanstatic int 400288522Sgrehanfwctl_response(uint32_t *retval) 401288522Sgrehan{ 402288522Sgrehan uint32_t *dp; 403341607Semaste ssize_t remlen; 404288522Sgrehan 405288522Sgrehan switch(rinfo.resp_count) { 406288522Sgrehan case 0: 407288522Sgrehan /* 4 x u32 header len + data */ 408288522Sgrehan *retval = 4*sizeof(uint32_t) + 409288522Sgrehan roundup(rinfo.resp_size, sizeof(uint32_t)); 410288522Sgrehan rinfo.resp_count++; 411288522Sgrehan break; 412288522Sgrehan case 1: 413288522Sgrehan *retval = rinfo.req_type; 414288522Sgrehan rinfo.resp_count++; 415288522Sgrehan break; 416288522Sgrehan case 2: 417288522Sgrehan *retval = rinfo.req_txid; 418288522Sgrehan rinfo.resp_count++; 419288522Sgrehan break; 420288522Sgrehan case 3: 421288522Sgrehan *retval = rinfo.resp_error; 422288522Sgrehan rinfo.resp_count++; 423288522Sgrehan break; 424288522Sgrehan default: 425288522Sgrehan remlen = rinfo.resp_size - rinfo.resp_off; 426288522Sgrehan dp = (uint32_t *) 427288522Sgrehan ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off); 428288522Sgrehan if (remlen >= sizeof(uint32_t)) { 429288522Sgrehan *retval = *dp; 430288522Sgrehan } else if (remlen > 0) { 431288522Sgrehan *retval = fwctl_send_rest(dp, remlen); 432288522Sgrehan } 433288522Sgrehan rinfo.resp_off += sizeof(uint32_t); 434288522Sgrehan break; 435288522Sgrehan } 436288522Sgrehan 437288522Sgrehan if (rinfo.resp_count > 3 && 438341607Semaste rinfo.resp_off >= rinfo.resp_size) { 439288522Sgrehan fwctl_response_done(); 440288522Sgrehan return (1); 441288522Sgrehan } 442288522Sgrehan 443288522Sgrehan return (0); 444288522Sgrehan} 445288522Sgrehan 446288522Sgrehan 447288522Sgrehan/* 448288522Sgrehan * i/o port handling. 449288522Sgrehan */ 450288522Sgrehanstatic uint8_t 451288522Sgrehanfwctl_inb(void) 452288522Sgrehan{ 453288522Sgrehan uint8_t retval; 454288522Sgrehan 455288522Sgrehan retval = 0xff; 456288522Sgrehan 457288522Sgrehan switch (be_state) { 458288522Sgrehan case IDENT_SEND: 459288522Sgrehan retval = sig[ident_idx++]; 460288522Sgrehan if (ident_idx >= sizeof(sig)) 461288522Sgrehan be_state = REQ; 462288522Sgrehan break; 463288522Sgrehan default: 464288522Sgrehan break; 465288522Sgrehan } 466288522Sgrehan 467288522Sgrehan return (retval); 468288522Sgrehan} 469288522Sgrehan 470288522Sgrehanstatic void 471288522Sgrehanfwctl_outw(uint16_t val) 472288522Sgrehan{ 473288522Sgrehan switch (be_state) { 474288522Sgrehan case IDENT_WAIT: 475288522Sgrehan if (val == 0) { 476288522Sgrehan be_state = IDENT_SEND; 477288522Sgrehan ident_idx = 0; 478288522Sgrehan } 479288522Sgrehan break; 480288522Sgrehan default: 481288522Sgrehan /* ignore */ 482288522Sgrehan break; 483288522Sgrehan } 484288522Sgrehan} 485288522Sgrehan 486288522Sgrehanstatic uint32_t 487288522Sgrehanfwctl_inl(void) 488288522Sgrehan{ 489288522Sgrehan uint32_t retval; 490288522Sgrehan 491288522Sgrehan switch (be_state) { 492288522Sgrehan case RESP: 493288522Sgrehan if (fwctl_response(&retval)) 494288522Sgrehan be_state = REQ; 495288522Sgrehan break; 496288522Sgrehan default: 497288522Sgrehan retval = 0xffffffff; 498288522Sgrehan break; 499288522Sgrehan } 500288522Sgrehan 501288522Sgrehan return (retval); 502288522Sgrehan} 503288522Sgrehan 504288522Sgrehanstatic void 505288522Sgrehanfwctl_outl(uint32_t val) 506288522Sgrehan{ 507288522Sgrehan 508288522Sgrehan switch (be_state) { 509288522Sgrehan case REQ: 510288522Sgrehan if (fwctl_request(val)) 511288522Sgrehan be_state = RESP; 512288522Sgrehan default: 513288522Sgrehan break; 514288522Sgrehan } 515288522Sgrehan 516288522Sgrehan} 517288522Sgrehan 518288522Sgrehanstatic int 519288522Sgrehanfwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 520288522Sgrehan uint32_t *eax, void *arg) 521288522Sgrehan{ 522288522Sgrehan 523288522Sgrehan if (in) { 524288522Sgrehan if (bytes == 1) 525288522Sgrehan *eax = fwctl_inb(); 526288522Sgrehan else if (bytes == 4) 527288522Sgrehan *eax = fwctl_inl(); 528288522Sgrehan else 529288522Sgrehan *eax = 0xffff; 530288522Sgrehan } else { 531288522Sgrehan if (bytes == 2) 532288522Sgrehan fwctl_outw(*eax); 533288522Sgrehan else if (bytes == 4) 534288522Sgrehan fwctl_outl(*eax); 535288522Sgrehan } 536288522Sgrehan 537288522Sgrehan return (0); 538288522Sgrehan} 539288522SgrehanINOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler); 540295124SgrehanINOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler); 541288522Sgrehan 542288522Sgrehanvoid 543288522Sgrehanfwctl_init(void) 544288522Sgrehan{ 545288522Sgrehan 546288522Sgrehan ops[OP_GET_LEN] = &fgetlen_info; 547288522Sgrehan ops[OP_GET] = &fgetval_info; 548288522Sgrehan 549288522Sgrehan be_state = IDENT_WAIT; 550288522Sgrehan} 551