├── .gitignore ├── LICENSE ├── README.md ├── libhexfive.h ├── multizone-amp-comm-protocol.pdf └── multizone-linux-driver.c /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Hex Five Security, Inc. 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without 4 | fee is hereby granted, provided that the above copyright notice and this permission notice appear 5 | in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 8 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE 9 | FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 11 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multizone-api 2 | 3 | Multi Zone API v1.1.1 4 | 5 | The RISC-V multi zone API definition is free as in “free speech” and free as in “free lunch”. The API definition is a free and open standard completely open sourced under ISC permissive license for “any use”. It is maintained by [Hex Five Security, Inc.](http://hex-five.com) and we welcome everyone’s direct contributions in the form of GitHub pull requests. Everyone can write their own implementation of the free and open API. We are strong supporter of the commercial open source movement and we actively promote the development of a global vibrant community!  6 | 7 | The MultiZone Security implementation provided by Hex Five includes MultiZone™ Configurator, MultiZone™ nanoKernel, and MultiZone™ Interzone Communications. These components are distributed as part of the [MultiZone™ Security SDK](https://github.com/hex-five/multizone-sdk), free of charge for evaluation only. Commercial use is restricted and requires Hex Five commercial license, sometime included with the hardware itself, but never ever royalties – as we hate royalties as much as you do! Hex Five’s core components are suitable for formal verification and everyone is welcome to perform formal verification and to publish models and results. In addition, Hex Five’s source code is always available to commercial licensees for inspection and detailed code review in a clean room environment. 8 | 9 | For more information see http://hex-five.com/faq 10 | 11 |
12 | 13 | |Function | Syntax and Function | Example | 14 | |---------|---------------------|---------| 15 | |ECALL_YIELD|`void ECALL_YIELD();`
Indicates to the nanoKernel scheduler that the Zone has nothing pressing to do and causes the nanoKernel to immediately move to the next Zone in context.| `ECALL_YIELD();`
In the case of a three zone implementation with a tick time of 10ms, the maximum time to come back to context is 20ms, faster if the other zones Yield as well.| 16 | |ECALL_WFI|`void ECALL_WFI();`
Unprivileged implementation of the Wait for Interrupt instruction WFI. The Wait for Interrupt instruction provides a hint to scheduler that the current zone can be paused until an interrupt might need servicing. If all zones are waiting for interrupt, the scheduler puts the core in a suspended low power state. Note that ECALL_WFI() is just a hint: a legal implementation is to implement ECALL_WFI() as an ECALL_YIELD(), in which case the core will never enter the low power state.| `int main (void){`
`... setup interrupt handlers`
`... while(1) {`
`...... do something`
`...... ECALL_WFI();`
`... }`
`}`

Typical implementation of the main loop of an event-driven zone.| 17 | |ECALL_SEND|`int ECALL_SEND([Zone #], [0-3][Int]);`
Send transmits a message from the current zone to the [Zone #]; the message size is an array of [4] integers and the nanoKernel manages transmission with no shared memory. The value returned is 1 if the receiving mailbox is empty and transmission successful or 0 if the receiving mailbox is full and transmission was blocked.|`int state = ECALL_SEND(1, {201, 0, 0 ,0});`
Sends an array to Zone 1 of {201, 0, 0, 0}; state = 1 if successful transmission.| 18 | |ECALL_RECV|`int ECALL_RECV[Zone #], [0-3][int]);`
Checks the mailbox of the current Zone for a message from the listed Zone #, if there is a new message the it returns 1, if no new messaage it returns 0. The value of the message is copied into the the array structure provided.| `int msg[4]={0,0,0,0};`
`int state = ECALL_RECV(1, msg);`
If a newmessage exists in the mailbox from zone 1, state = 1 and it copies it to msg, otherwise state = 0.| 19 | |ECALL_CSRS_MIE|`void ECALL_CSRS_MIE();`
Secure user-mode emulation of the Machine Status Register (mstatus) MIE bit. Enables all interrupts (PLIC + CLINT) mapped to the zone including the soft timer (trap 0x3). The operation is atomic with respect to the context of the zone.| `ECALL_CSRS_MIE();`| 20 | |ECALL_CSRC_MIE|`void ECALL_CSRC_MIE();`
Secure user-mode emulation of the Machine Status Register (mstatus) MIE bit. Disables all interrupts (PLIC + CLINT) mapped to the zone including the soft timer (trap 0x3). The operation is atomic with respect to the context of the zone.| `ECALL_CSRC_MIE();`| 21 | |ECALL_TRP_VECT |`void ECALL_TRP_VECT([Exception Code], [Trap Handler])`
Registers a handler against a trap generated by anauthorized instructions; the TRAP #s are defined in the RISC-V Privileged Architectures definition V1.1, Table 3.6 Interrupt 0 types. https://riscv.org/specifications/privileged-isa/ |`ECALL_TRP_VECT(0x0, trap_0x0_handler);`
Where trap_0x0_handler is registered at the User level of privilege with:
`Void trap_0x0_handler(void)__attribute__((interrupt("user")));`
`void trap_0x0_handler(void){`
` // Your handler code here`
`}`| 22 | |ECALL_IRQ_VECT |`void ECALL_IRQ_VECT([Interrupt #], [Trap Handler])`
Registers a handler for an interrupt that has been assigned to a Zone in the multizone.cfg file.
When an interrupt occurs, the nanoKernel will immediately pull the zone assigned to that interrupt into context and execute the registered interrupt handler. |`ECALL_IRQ_VECT(11, button_0_handler);`
Where button_0_handler is a registered at the user level of privilege with:
`void button_1_handler(void)__attribute__((interrupt("user")));`
`void button_1_handler(void){`
` // interrupt handler here`
`}`| 23 | |ECALL_CSRW_MTIMECMP |`void ECALL_CSRW_MTIMECMP(uint64_t)`
Secure user-mode emulation of the machine-mode timer compare register (mtimecmp). Causes a trap 0x3 exception when the mtime register contains a value greater than or equal to the value assigned. Each zone has its own secure instance of timer and trap handler. Per RISC-V specs this is a one-shot timer: once set it will execute its callback function only once. Note that mtime and mtimecmp size is 64-bit even on rv32 architecture. Registering the trap 0x3 handler sets the value of mtimecmp to zero to prevent spurious interrupts. If the timer is set but no handler is registered the exception is ignored. | `#include `
`...`

`void trap_0x3_handler(void)__attribute__((interrupt("user")));`
`void trap_0x3_handler(void){`
`// do something `
` // restart the timer`
` uint64_t T = 10; // ms `
` uint64_t T0 = ECALL_CSRR_MTIME();`
`uint64_t T1 = T0 + T*32768/1000; `
` ECALL_CSRR_MTIMECMP(T1); `

`} `
`...`
`main () { `
`ECALL_TRP_VECT(0x3, trap_0x3_handler); // register 0x3 Soft timer `
`while(1){`
` // do many things `
`} `
` } `
24 | |ECALL_CSRR_MTIME|`Int64 ECALL_CSRR_MTIME()`
Returns MTIME to a variable in a zone, MTIME is a privileged registered normally only available in M mode. |`Int64 mtime = ECALL_CSRR_MTIME();`| 25 | |ECALL_CSRR_MCYCLE|`Int64 ECALL_CSRR_MCYCLE()`
Returns MCYCLE to a variable in a zone, MCYCLE is a privileged registered normally only available in M mode. |`Int64 mcycle = ECALL_CSRR_MCYCLE();` 26 | |ECALL_CSRR_MINSTR|`Int64 ECALL_CSRR_MINSTR()`
Returns MINSTR to a variable in a zone, MINSTR is a privileged registered normally only available in M mode. |`Int64 minstr = ECALL_CSRR_MINSTR();` 27 | |ECALL_CSRR_MHPMC3|`Int64 ECALL_CSRR_MHPMC3()`
Returns MHPMC3 to a variable in a zone, MHPMC3 is a privileged registered normally only available in M mode. |`Int64 mhpmc3 = ECALL_CSRR_MHPMC3();` 28 | |ECALL_CSRR_MHPMC4|`Int64 ECALL_CSRR_MHPMC4()`
Returns MHPMC4 to a variable in a zone, MHPMC4 is a privileged registered normally only available in M mode. |`Int64 mhpmc3 = ECALL_CSRR_MHPMC4();` 29 | |ECALL_CSRR_MISA|`Int64 ECALL_CSRR_MISA()`
Returns MISA to a variable in a zone, MISA is a privileged registered normally only available in M mode. |`Int64 misa = ECALL_CSRR_MISA();` 30 | |ECALL_CSRR_MVENDID|`Int64 ECALL_CSRR_MVENDID()`
Returns MVENDID to a variable in a zone, MVENDID is a privileged registered normally only available in M mode. |`Int64 misa = ECALL_CSRR_MVENDID();` 31 | |ECALL_CSRR_MARCHID|`Int64 ECALL_CSRR_MARCHID()`
Returns MARCHID to a variable in a zone, MARCHID is a privileged registered normally only available in M mode. |`Int64 marchid = ECALL_CSRR_MARCHID();` 32 | |ECALL_CSRR_MIMPID|`Int64 ECALL_CSRR_MIMPID()`
Returns MIMPID to a variable in a zone, MIMPID is a privileged registered normally only available in M mode. |`Int64 mimpid = ECALL_CSRR_ MIMPID ();` 33 | |ECALL_CSRR_MHARTID|`Int64 ECALL_CSRR_MHARTID()`
Returns MHARTID to a variable in a zone, MHARTID is a privileged registered normally only available in M mode. |`Int64 mhardid = ECALL_CSRR_ MHARTID ();` 34 | -------------------------------------------------------------------------------- /libhexfive.h: -------------------------------------------------------------------------------- 1 | /* Copyright(C) 2018 Hex Five Security, Inc. */ 2 | 3 | #include 4 | 5 | #ifndef LIBHEXFIVE_H_ 6 | #define LIBHEXFIVE_H_ 7 | 8 | void ECALL_YIELD(); 9 | void ECALL_WFI(); 10 | 11 | int ECALL_SEND(int, void *); 12 | int ECALL_RECV(int, void *); 13 | 14 | void ECALL_TRP_VECT(int, void *); 15 | void ECALL_IRQ_VECT(int, void *); 16 | 17 | void ECALL_CSRS_MIE(); 18 | void ECALL_CSRC_MIE(); 19 | 20 | void ECALL_CSRW_MTIMECMP(uint64_t); 21 | 22 | uint64_t ECALL_CSRR_MTIME(); 23 | uint64_t ECALL_CSRR_MCYCLE(); 24 | uint64_t ECALL_CSRR_MINSTR(); 25 | uint64_t ECALL_CSRR_MHPMC3(); 26 | uint64_t ECALL_CSRR_MHPMC4(); 27 | 28 | uint64_t ECALL_CSRR_MISA(); 29 | uint64_t ECALL_CSRR_MVENDID(); 30 | uint64_t ECALL_CSRR_MARCHID(); 31 | uint64_t ECALL_CSRR_MIMPID(); 32 | uint64_t ECALL_CSRR_MHARTID(); 33 | 34 | #endif /* LIBHEXFIVE_H_ */ 35 | -------------------------------------------------------------------------------- /multizone-amp-comm-protocol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hex-five/multizone-api/dea206945e6f39e6e287924dbc5d8cd31cf94bdf/multizone-amp-comm-protocol.pdf -------------------------------------------------------------------------------- /multizone-linux-driver.c: -------------------------------------------------------------------------------- 1 | /* Copyright(C) 2019 Hex Five Security, Inc. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define VALID_BIT 63 25 | #define LOCK_BIT 0 26 | #define MAX_ZONES 4 27 | #define TRANSFER_SIZE 4 28 | 29 | static struct of_device_id multizone_match_table[] = { { .compatible = 30 | "hexfive,multizone", }, { } }; 31 | MODULE_DEVICE_TABLE(of, multizone_match_table); 32 | 33 | struct mbox_entry { 34 | unsigned long valid; 35 | u32 msg[4]; 36 | }; 37 | 38 | struct multizone_zone; 39 | 40 | struct multizone { 41 | unsigned int irq; 42 | unsigned int n_zones; 43 | struct mbox_entry *send; 44 | struct mbox_entry *recv; 45 | struct multizone_zone *zones[MAX_ZONES + 1]; 46 | }; 47 | 48 | struct multizone_zone { 49 | struct uart_port port; 50 | spinlock_t lock; 51 | int kicked; 52 | struct multizone *m; 53 | struct task_struct *zone_thread; 54 | bool irq_registered; 55 | unsigned int zone; 56 | }; 57 | 58 | static void multizone_serial_stop_tx(struct uart_port *port); 59 | 60 | static void multizone_serial_transmit_chars(struct multizone_zone *mz) { 61 | struct multizone *m = mz->m; 62 | volatile struct mbox_entry *send_entry = &m->send[mz->zone - 1]; 63 | volatile char *send_buf = (char*) (&send_entry->msg[0]); 64 | struct circ_buf *xmit = &mz->port.state->xmit; 65 | int i; 66 | unsigned long flags; 67 | volatile unsigned long lock = 0x01; 68 | 69 | spin_lock_irqsave(&mz->lock, flags); 70 | 71 | /* Atomic: Aquire lock and update send_entry->valid */ 72 | asm volatile("amoswap.d.aq %0, %1, (%2)" : "=r" (lock) : "r" (lock), "r" (&send_entry->valid) : "memory"); 73 | 74 | if (!test_bit(LOCK_BIT, &lock)) { 75 | 76 | if (!test_bit(VALID_BIT, &lock)) { 77 | if (uart_circ_empty(xmit) || uart_tx_stopped(&mz->port)) { 78 | multizone_serial_stop_tx(&mz->port); 79 | /* Atomic: Release lock and update send_entry->valid */ 80 | asm volatile("amoswap.d.rl %0, %1, (%2)" : "=r" (lock) : "r" (lock), "r" (&send_entry->valid) : "memory"); 81 | spin_unlock_irqrestore(&mz->lock, flags); 82 | return; 83 | } 84 | for (i = 0; 85 | i < TRANSFER_SIZE && i < mz->port.fifosize 86 | && !uart_circ_empty(xmit); i++) { 87 | send_buf[i] = xmit->buf[xmit->tail]; 88 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 89 | mz->port.icount.tx++; 90 | } 91 | for (; i < TRANSFER_SIZE; i++) { 92 | send_buf[i] = 0; 93 | } 94 | set_bit(VALID_BIT, &lock); 95 | } 96 | 97 | /* Atomic: Release lock and update send_entry->valid */ 98 | asm volatile("amoswap.d.rl %0, %1, (%2)" : "=r" (lock) : "r" (lock), "r" (&send_entry->valid) : "memory"); 99 | 100 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 101 | uart_write_wakeup(&mz->port); 102 | 103 | if (uart_circ_empty(xmit)) 104 | multizone_serial_stop_tx(&mz->port); 105 | } 106 | 107 | spin_unlock_irqrestore(&mz->lock, flags); 108 | } 109 | 110 | void multizone_check_buffers(struct multizone_zone *mz) { 111 | struct multizone *m = mz->m; 112 | volatile struct mbox_entry *recv_entry = &m->recv[mz->zone - 1]; 113 | struct uart_port *port = &mz->port; 114 | int c; 115 | volatile char *buf = (char*) &recv_entry->msg[0]; 116 | unsigned long flags; 117 | volatile unsigned long lock = 0x01; 118 | 119 | spin_lock_irqsave(&mz->lock, flags); 120 | 121 | /* Atomic: Aquire lock and update recv_entry->valid */ 122 | asm volatile("amoswap.d.aq %0, %1, (%2)" : "=r" (lock) : "r" (lock), "r" (&recv_entry->valid) : "memory"); 123 | 124 | if (!test_bit(LOCK_BIT, &lock)) { 125 | 126 | if (test_bit(VALID_BIT, &lock)) { 127 | for (c = 0; buf[c] != 0 && c < TRANSFER_SIZE; c++) { 128 | port->icount.rx++; 129 | uart_insert_char(port, 0, 0, buf[c], TTY_NORMAL); 130 | } 131 | tty_flip_buffer_push(&port->state->port); 132 | clear_bit(VALID_BIT, &lock); 133 | } 134 | /* Atomic: Release lock and update recv_entry->valid */ 135 | asm volatile("amoswap.d.rl %0, %1, (%2)" : "=r" (lock) : "r" (lock), "r" (&recv_entry->valid) : "memory"); 136 | } 137 | 138 | spin_unlock_irqrestore(&mz->lock, flags); 139 | 140 | multizone_serial_transmit_chars(mz); 141 | } 142 | 143 | irqreturn_t multizone_irq(int irq, void *data) { 144 | struct multizone *m = (struct multizone*) data; 145 | int i; 146 | 147 | for (i = 1; i <= m->n_zones; i++) { 148 | struct multizone_zone *mz = m->zones[i]; 149 | 150 | mz->kicked = 1; 151 | 152 | if (mz->zone_thread) { 153 | wake_up_process(mz->zone_thread); 154 | } 155 | } 156 | 157 | return IRQ_HANDLED; 158 | } 159 | 160 | static int zone_thread(void *data) { 161 | struct multizone_zone *mz = (struct multizone_zone*) data; 162 | while (!kthread_should_stop()) { 163 | mz->kicked = 0; 164 | multizone_check_buffers(mz); 165 | if (mz->kicked) 166 | continue; 167 | 168 | schedule_timeout_interruptible(1); 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | static unsigned int multizone_serial_tx_empty(struct uart_port *port) { 175 | return 0; 176 | } 177 | 178 | static void multizone_serial_set_mctrl(struct uart_port *port, 179 | unsigned int mctrl) { 180 | 181 | } 182 | 183 | static unsigned int multizone_serial_get_mctrl(struct uart_port *port) { 184 | return 0; 185 | } 186 | 187 | static void multizone_serial_stop_tx(struct uart_port *port) { 188 | 189 | } 190 | 191 | static void multizone_serial_start_tx(struct uart_port *port) { 192 | struct multizone_zone 193 | *mz = container_of(port, struct multizone_zone, port); 194 | 195 | multizone_serial_transmit_chars (mz); 196 | } 197 | 198 | static void multizone_serial_stop_rx(struct uart_port *port) { 199 | 200 | } 201 | 202 | static void multizone_serial_enable_ms(struct uart_port *port) { 203 | 204 | } 205 | 206 | static void multizone_serial_break_ctl(struct uart_port *port, 207 | int break_state) { 208 | 209 | } 210 | 211 | static int multizone_serial_startup(struct uart_port *port) { 212 | struct multizone_zone 213 | *mz = container_of(port, struct multizone_zone, port); 214 | mz->zone_thread = kthread_run(zone_thread, mz, "zone%d", mz->zone); 215 | return 0; 216 | } 217 | 218 | static void multizone_serial_shutdown(struct uart_port *port) { 219 | struct multizone_zone 220 | *mz = container_of(port, struct multizone_zone, port); 221 | kthread_stop(mz->zone_thread); 222 | } 223 | 224 | static void multizone_serial_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old){ 225 | int rate; 226 | 227 | rate = uart_get_baud_rate(port, new, old, 0, 115200); 228 | uart_update_timeout(port, new->c_cflag, rate); 229 | } 230 | 231 | static const char* multizone_serial_type(struct uart_port *port) { 232 | return "multizone"; 233 | } 234 | 235 | static void multizone_serial_release_port(struct uart_port *port) { 236 | 237 | } 238 | 239 | static int multizone_serial_request_port(struct uart_port *port) { 240 | return 0; 241 | } 242 | 243 | static void multizone_serial_config_port(struct uart_port *port, int flags) { 244 | 245 | } 246 | 247 | static int multizone_serial_verify_port(struct uart_port *port, 248 | struct serial_struct *ser) { 249 | return 0; 250 | } 251 | 252 | static struct uart_ops multizone_serial_ops = { .tx_empty = 253 | multizone_serial_tx_empty, .set_mctrl = multizone_serial_set_mctrl, 254 | .get_mctrl = multizone_serial_get_mctrl, .stop_tx = 255 | multizone_serial_stop_tx, .start_tx = multizone_serial_start_tx, 256 | .stop_rx = multizone_serial_stop_rx, .enable_ms = 257 | multizone_serial_enable_ms, .break_ctl = 258 | multizone_serial_break_ctl, .startup = multizone_serial_startup, 259 | .shutdown = multizone_serial_shutdown, .set_termios = 260 | multizone_serial_set_termios, .type = multizone_serial_type, 261 | .release_port = multizone_serial_release_port, .request_port = 262 | multizone_serial_request_port, .config_port = 263 | multizone_serial_config_port, .verify_port = 264 | multizone_serial_verify_port, }; 265 | 266 | static struct uart_driver multizone_serial_driver = { .owner = THIS_MODULE, 267 | .driver_name = "multizone", .dev_name = "multizone", .minor = 0, .nr = 268 | MAX_ZONES + 1, }; 269 | 270 | static int multizone_probe(struct platform_device *pdev) { 271 | struct multizone *m; 272 | struct resource *send_mem = &pdev->resource[0]; 273 | struct resource *recv_mem = &pdev->resource[1]; 274 | int i, ret = 0; 275 | 276 | m = devm_kzalloc(&pdev->dev, sizeof(struct multizone), GFP_KERNEL); 277 | if (!m) { 278 | ret = -ENOMEM; 279 | goto fail; 280 | } 281 | 282 | platform_set_drvdata(pdev, m); 283 | 284 | ret = of_property_read_u32(pdev->dev.of_node, "zones", &m->n_zones); 285 | if (ret < 0) { 286 | dev_warn(&pdev->dev, 287 | "MultiZone #zones not specified, defaulting to 4\n"); 288 | m->n_zones = 4; 289 | } 290 | 291 | m->send = devm_memremap(&pdev->dev, send_mem->start, 292 | resource_size(send_mem), MEMREMAP_WT); 293 | m->recv = devm_memremap(&pdev->dev, recv_mem->start, 294 | resource_size(recv_mem), MEMREMAP_WT); 295 | if (!m->send || !m->recv) { 296 | ret = -ENOMEM; 297 | goto fail; 298 | } 299 | 300 | for (i = 1; i <= m->n_zones; i++) { 301 | struct multizone_zone *mz; 302 | 303 | mz = devm_kzalloc(&pdev->dev, sizeof(struct multizone_zone), 304 | GFP_KERNEL); 305 | if (!mz) 306 | return -ENOMEM; 307 | 308 | m->zones[i] = mz; 309 | mz->m = m; 310 | mz->zone = i; 311 | mz->port.dev = &pdev->dev; 312 | mz->port.fifosize = TRANSFER_SIZE; 313 | mz->port.line = i; 314 | mz->port.ops = &multizone_serial_ops; 315 | mz->port.type = PORT_8250; 316 | spin_lock_init(&mz->lock); 317 | 318 | ret = uart_add_one_port(&multizone_serial_driver, &mz->port); 319 | if (ret != 0) { 320 | dev_err(&pdev->dev, "Zone %d, could not add UART port, error %d\n", 321 | i, ret); 322 | } 323 | } 324 | 325 | m->irq = 16; 326 | ret = request_irq(m->irq, multizone_irq, IRQF_SHARED, dev_name(&pdev->dev), 327 | m); 328 | if (ret) { 329 | m->irq = 0; 330 | dev_err(&pdev->dev, "Could not register interrupts, error %d\n", ret); 331 | } 332 | 333 | dev_info(&pdev->dev, 334 | "MultiZone loaded, zones = %d, send buffer @ %llx, recv buffer @ %llx\n", 335 | m->n_zones, send_mem->start, recv_mem->start); 336 | 337 | return 0; 338 | 339 | fail: return ret; 340 | } 341 | 342 | static int multizone_remove(struct platform_device *pdev) { 343 | struct multizone *m = (struct multizone*) platform_get_drvdata(pdev); 344 | int i; 345 | 346 | if (m->irq) 347 | free_irq(m->irq, m); 348 | 349 | for (i = 1; i <= m->n_zones; i++) { 350 | uart_remove_one_port(&multizone_serial_driver, &m->zones[i]->port); 351 | } 352 | 353 | dev_info(&pdev->dev, "MultiZone unloaded!\n"); 354 | return 0; 355 | } 356 | 357 | static struct platform_driver multizone_platform_driver = { .probe = 358 | multizone_probe, .remove = multizone_remove, .driver = { .owner = 359 | THIS_MODULE, .name = "multizone_driver", .of_match_table = of_match_ptr( 360 | multizone_match_table), }, }; 361 | 362 | int __init multizone_init(void) 363 | { 364 | struct tty_driver *tty_drv; 365 | int ret; 366 | 367 | ret = uart_register_driver(&multizone_serial_driver); 368 | if (ret) goto fail; 369 | 370 | tty_drv = multizone_serial_driver.tty_driver; 371 | 372 | tty_drv->init_termios = tty_std_termios; 373 | tty_drv->init_termios.c_iflag = 0; 374 | tty_drv->init_termios.c_oflag = 0; 375 | tty_drv->init_termios.c_lflag = 0; 376 | tty_drv->flags = TTY_DRIVER_REAL_RAW; 377 | 378 | ret = platform_driver_register(&multizone_platform_driver); 379 | if (ret) goto fail_platform; 380 | 381 | return 0; 382 | 383 | fail_platform: 384 | uart_unregister_driver(&multizone_serial_driver); 385 | fail: 386 | return ret; 387 | } 388 | 389 | void __exit multizone_exit(void) 390 | { 391 | platform_driver_unregister(&multizone_platform_driver); 392 | uart_unregister_driver(&multizone_serial_driver); 393 | } 394 | 395 | module_init (multizone_init); 396 | module_exit (multizone_exit); 397 | 398 | MODULE_DESCRIPTION("MultiZone Linux Driver"); 399 | MODULE_LICENSE("ISC"); 400 | MODULE_AUTHOR("Hex Five Security, Inc. "); 401 | --------------------------------------------------------------------------------