@@ -293,45 +293,110 @@ static void load_dtb(char **ram_loc, vm_attr_t *attr)
293293{
294294#include "minimal_dtb.h"
295295 char * bootargs = attr -> data .system .bootargs ;
296- char * vblk = attr -> data .system .vblk_device ;
296+ char * * vblk = attr -> data .system .vblk_device ;
297297 char * blob = * ram_loc ;
298298 char * buf ;
299299 size_t len ;
300300 int node , err ;
301301 int totalsize ;
302302
303- memcpy (blob , minimal , sizeof (minimal ));
303+ #define DTB_EXPAND_SIZE 1024 /* or more if needed */
304+
305+ /* Allocate enough memory for DTB + extra room */
306+ size_t minimal_len = ARRAY_SIZE (minimal );
307+ void * dtb_buf = calloc (minimal_len + DTB_EXPAND_SIZE , sizeof (uint8_t ));
308+ assert (dtb_buf );
309+
310+ /* Expand it to a usable DTB blob */
311+ err = fdt_open_into (minimal , dtb_buf , minimal_len + DTB_EXPAND_SIZE );
312+ if (err < 0 ) {
313+ rv_log_error ("fdt_open_into fails\n" );
314+ exit (EXIT_FAILURE );
315+ }
304316
305317 if (bootargs ) {
306- node = fdt_path_offset (blob , "/chosen" );
318+ node = fdt_path_offset (dtb_buf , "/chosen" );
307319 assert (node > 0 );
308320
309321 len = strlen (bootargs ) + 1 ;
310322 buf = malloc (len );
311323 assert (buf );
312324 memcpy (buf , bootargs , len - 1 );
313325 buf [len ] = 0 ;
314- err = fdt_setprop (blob , node , "bootargs" , buf , len + 1 );
326+ err = fdt_setprop (dtb_buf , node , "bootargs" , buf , len + 1 );
315327 if (err == - FDT_ERR_NOSPACE ) {
316- blob = realloc_property (blob , node , "bootargs" , len );
317- err = fdt_setprop (blob , node , "bootargs" , buf , len );
328+ dtb_buf = realloc_property (dtb_buf , node , "bootargs" , len );
329+ err = fdt_setprop (dtb_buf , node , "bootargs" , buf , len );
318330 }
319331 free (buf );
320332 assert (!err );
321333 }
322334
323- /* remove the vblk node from soc if it is not specified */
324- if (!vblk ) {
325- int subnode ;
326- node = fdt_path_offset (blob , "/soc@F0000000" );
335+ if (vblk ) {
336+ int node = fdt_path_offset (dtb_buf , "/soc@F0000000" );
327337 assert (node >= 0 );
328338
329- subnode = fdt_subnode_offset (blob , node , "virtio@4200000" );
330- assert (subnode >= 0 );
339+ uint32_t base_addr = 0x4000000 ;
340+ uint32_t addr_offset = 0x100000 ;
341+ uint32_t size = 0x200 ;
342+
343+ uint32_t next_addr = base_addr ;
344+ uint32_t next_irq = 1 ;
345+
346+ /* scan existing nodes to get next addr and irq */
347+ int subnode ;
348+ fdt_for_each_subnode (subnode , dtb_buf , node )
349+ {
350+ const char * name = fdt_get_name (dtb_buf , subnode , NULL );
351+ assert (name );
352+
353+ uint32_t addr = strtoul (name + 7 , NULL , 16 );
354+ if (addr == next_addr )
355+ next_addr = addr + addr_offset ;
356+
357+ const fdt32_t * irq_prop =
358+ fdt_getprop (dtb_buf , subnode , "interrupts" , NULL );
359+ if (irq_prop ) {
360+ uint32_t irq = fdt32_to_cpu (* irq_prop );
361+ if (irq == next_irq )
362+ next_irq = irq + 1 ;
363+ }
364+ }
365+ /* set IRQ for virtio block, see devices/virtio.h */
366+ attr -> vblk_irq_base = next_irq ;
367+
368+ /* adding new virtio block nodes */
369+ for (int i = 0 ; i < attr -> vblk_cnt ; i ++ ) {
370+ uint32_t new_addr = next_addr + i * addr_offset ;
371+ uint32_t new_irq = next_irq + i ;
372+
373+ char node_name [32 ];
374+ snprintf (node_name , sizeof (node_name ), "virtio@%x" , new_addr );
375+
376+ int subnode = fdt_add_subnode (dtb_buf , node , node_name );
377+ if (subnode == - FDT_ERR_NOSPACE ) {
378+ rv_log_warn ("add subnode no space!\n" );
379+ }
380+ assert (subnode >= 0 );
331381
332- assert (fdt_del_node (blob , subnode ) == 0 );
382+ /* compatible = "virtio,mmio" */
383+ assert (fdt_setprop_string (dtb_buf , subnode , "compatible" ,
384+ "virtio,mmio" ) == 0 );
385+
386+ /* reg = <new_addr size> */
387+ uint32_t reg [2 ] = {cpu_to_fdt32 (new_addr ), cpu_to_fdt32 (size )};
388+ assert (fdt_setprop (dtb_buf , subnode , "reg" , reg , sizeof (reg )) == 0 );
389+
390+ /* interrupts = <new_irq> */
391+ uint32_t irq = cpu_to_fdt32 (new_irq );
392+ assert (fdt_setprop (dtb_buf , subnode , "interrupts" , & irq ,
393+ sizeof (irq )) == 0 );
394+ }
333395 }
334396
397+ memcpy (blob , dtb_buf , minimal_len + DTB_EXPAND_SIZE );
398+ free (dtb_buf );
399+
335400 totalsize = fdt_totalsize (blob );
336401 * ram_loc += totalsize ;
337402 return ;
@@ -406,28 +471,36 @@ static void rv_fsync_device()
406471 *
407472 * vblk is optional, so it could be NULL
408473 */
409- if (attr -> vblk ) {
410- if (attr -> vblk -> disk_fd >= 3 ) {
411- if (attr -> vblk -> device_features & VIRTIO_BLK_F_RO ) /* readonly */
412- goto end ;
413-
414- if (pwrite (attr -> vblk -> disk_fd , attr -> vblk -> disk ,
415- attr -> vblk -> disk_size , 0 ) == -1 ) {
416- rv_log_error ("pwrite block device failed: %s" , strerror (errno ));
417- return ;
474+ if (attr -> vblk_cnt ) {
475+ for (int i = 0 ; i < attr -> vblk_cnt ; i ++ ) {
476+ virtio_blk_state_t * vblk = attr -> vblk [i ];
477+ if (vblk -> disk_fd >= 3 ) {
478+ if (vblk -> device_features & VIRTIO_BLK_F_RO ) /* readonly */
479+ goto end ;
480+
481+ if (pwrite (vblk -> disk_fd , vblk -> disk , vblk -> disk_size , 0 ) ==
482+ -1 ) {
483+ rv_log_error ("pwrite block device failed: %s" ,
484+ strerror (errno ));
485+ return ;
486+ }
487+
488+ if (fsync (vblk -> disk_fd ) == -1 ) {
489+ rv_log_error ("fsync block device failed: %s" ,
490+ strerror (errno ));
491+ return ;
492+ }
493+ rv_log_info ("Sync block device OK" );
494+
495+ end :
496+ close (vblk -> disk_fd );
418497 }
419498
420- if (fsync (attr -> vblk -> disk_fd ) == -1 ) {
421- rv_log_error ("fsync block device failed: %s" , strerror (errno ));
422- return ;
423- }
424- rv_log_info ("Sync block device OK" );
425-
426- end :
427- close (attr -> vblk -> disk_fd );
499+ vblk_delete (vblk );
428500 }
429501
430- vblk_delete (attr -> vblk );
502+ free (attr -> vblk );
503+ free (attr -> disk );
431504 }
432505}
433506#endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */
@@ -548,6 +621,9 @@ riscv_t *rv_create(riscv_user_t rv_attr)
548621 * *----------------*----------------*-------*
549622 */
550623
624+ /* load_dtb needs the count to add the virtio block subnode dynamically */
625+ attr -> vblk_cnt = attr -> data .system .vblk_device_cnt ;
626+
551627 char * ram_loc = (char * ) attr -> mem -> mem_base ;
552628 map_file (& ram_loc , attr -> data .system .kernel );
553629 rv_log_info ("Kernel loaded" );
@@ -590,36 +666,47 @@ riscv_t *rv_create(riscv_user_t rv_attr)
590666 attr -> uart -> out_fd = attr -> fd_stdout ;
591667
592668 /* setup virtio-blk */
593- attr -> vblk = NULL ;
594- if (attr -> data .system .vblk_device ) {
669+ attr -> vblk_mmio_base_hi = 0x41 ;
670+ attr -> vblk_mmio_max_hi = attr -> vblk_mmio_base_hi + attr -> vblk_cnt ;
671+
672+ attr -> vblk = malloc (sizeof (virtio_blk_state_t * ) * attr -> vblk_cnt );
673+ assert (attr -> vblk );
674+ attr -> disk = malloc (sizeof (uint32_t * ) * attr -> vblk_cnt );
675+ assert (attr -> disk );
676+
677+ if (attr -> vblk_cnt ) {
678+ for (int i = 0 ; i < attr -> vblk_cnt ; i ++ ) {
595679/* Currently, only used for block image path and permission */
596680#define MAX_OPTS 2
597- char * vblk_opts [MAX_OPTS ] = {NULL };
598- int vblk_opt_idx = 0 ;
599- char * opt = strtok (attr -> data .system .vblk_device , "," );
600- while (opt ) {
601- if (vblk_opt_idx == MAX_OPTS ) {
602- rv_log_error ("Too many arguments for vblk" );
603- break ;
681+ char * vblk_device_str = attr -> data .system .vblk_device [i ];
682+ char * vblk_opts [MAX_OPTS ] = {NULL };
683+ int vblk_opt_idx = 0 ;
684+ char * opt = strtok (vblk_device_str , "," );
685+ while (opt ) {
686+ if (vblk_opt_idx == MAX_OPTS ) {
687+ rv_log_error ("Too many arguments for vblk" );
688+ break ;
689+ }
690+ vblk_opts [vblk_opt_idx ++ ] = opt ;
691+ opt = strtok (NULL , "," );
604692 }
605- vblk_opts [vblk_opt_idx ++ ] = opt ;
606- opt = strtok (NULL , "," );
607- }
608- char * vblk_device = vblk_opts [0 ];
609- char * vblk_readonly = vblk_opts [1 ];
610-
611- bool readonly = false;
612- if (vblk_readonly ) {
613- if (strcmp (vblk_readonly , "readonly" ) != 0 ) {
614- rv_log_error ("Unknown vblk option: %s" , vblk_readonly );
615- exit (EXIT_FAILURE );
693+ char * vblk_device = vblk_opts [0 ];
694+ char * vblk_readonly = vblk_opts [1 ];
695+
696+ bool readonly = false;
697+ if (vblk_readonly ) {
698+ if (strcmp (vblk_readonly , "readonly" ) != 0 ) {
699+ rv_log_error ("Unknown vblk option: %s" , vblk_readonly );
700+ exit (EXIT_FAILURE );
701+ }
702+ readonly = true;
616703 }
617- readonly = true;
618- }
619704
620- attr -> vblk = vblk_new ();
621- attr -> vblk -> ram = (uint32_t * ) attr -> mem -> mem_base ;
622- attr -> disk = virtio_blk_init (attr -> vblk , vblk_device , readonly );
705+ attr -> vblk [i ] = vblk_new ();
706+ attr -> vblk [i ]-> ram = (uint32_t * ) attr -> mem -> mem_base ;
707+ attr -> disk [i ] =
708+ virtio_blk_init (attr -> vblk [i ], vblk_device , readonly );
709+ }
623710 }
624711
625712 capture_keyboard_input ();
0 commit comments